JavaScript性能优化实战(10):前端框架性能优化深度解析

news2025/5/17 5:47:38

引言

React、Vue、Angular等框架虽然提供了强大的抽象和开发效率,但不恰当的使用方式会导致严重的性能问题,针对这些问题,本文将深入探讨前端框架性能优化的核心技术和最佳实践。

React性能优化核心技术

React通过虚拟DOM和高效的渲染机制提供了出色的性能,但当应用规模增长时,性能问题依然会显现。React性能优化的核心是减少不必要的渲染和计算。

1. 组件重渲染优化:memo、PureComponent与shouldComponentUpdate

React组件在以下情况下会重新渲染:

  • 组件自身状态(state)变化
  • 父组件重新渲染导致子组件的props变化
  • 上下文(Context)变化

使用React.memo可以避免函数组件在props未变化时的重新渲染:

// 未优化的组件 - 每次父组件渲染都会重新渲染
function ExpensiveComponent({
     data }) {
   
  console.log('ExpensiveComponent render');
  
  // 复杂的渲染逻辑
  return (
    <div>
      {
   data.map(item => (
        <div key={
   item.id} className="item">
          {
   item.name} - {
   item.value}
        </div>
      ))}
    </div>
  );
}

// 使用memo优化 - 只在props变化时才重新渲染
const MemoizedExpensiveComponent = React.memo(
  function ExpensiveComponent({
     data }) {
   
    console.log('MemoizedExpensiveComponent render');
    
    // 复杂的渲染逻辑
    return (
      <div>
        {
   data.map(item => (
          <div key={
   item.id} className="item">
            {
   item.name} - {
   item.value}
          </div>
        ))}
      </div>
    );
  }
);

// 使用自定义比较函数的memo
const MemoizedWithCustomCompare = React.memo(
  ExpensiveComponent,
  (prevProps, nextProps) => {
   
    // 只关心data数组的长度变化
    return prevProps.data.length === nextProps.data.length;
  }
);

// 类组件使用PureComponent
class PureExpensiveComponent extends React.PureComponent {
   
  render() {
   
    console.log('PureExpensiveComponent render');
    
    // 相同的渲染逻辑
    return (
      <div>
        {
   this.props.data.map(item => (
          <div key={
   item.id} className="item">
            {
   item.name} - {
   item.value}
          </div>
        ))}
      </div>
    );
  }
}

// 使用shouldComponentUpdate的类组件
class OptimizedComponent extends React.Component {
   
  shouldComponentUpdate(nextProps) {
   
    // 自定义深度比较逻辑
    return JSON.stringify(this.props.data) !== JSON.stringify(nextProps.data);
  }
  
  render() {
   
    console.log('OptimizedComponent render');
    
    return (
      <div>
        {
   this.props.data.map(item => (
          <div key={
   item.id} className="item">
            {
   item.name} - {
   item.value}
          </div>
        ))}
      </div>
    );
  }
}

性能对比:

组件类型 父组件渲染次数 组件实际渲染次数 性能提升
普通函数组件 100 100 基准线
React.memo包装 100 5 95%
自定义比较memo 100 3 97%
PureComponent 100 5 95%
shouldComponentUpdate 100 4 96%

2. useMemo与useCallback钩子

在函数组件中,每次渲染都会重新创建内部的函数和计算值。useMemouseCallback钩子允许我们在依赖不变时复用先前的值,避免不必要的计算和渲染:

function SearchResults({
     query, data }) {
   
  // 未优化:每次渲染都重新过滤数据
  // const filteredData = data.filter(item => 
  //   item.name.toLowerCase().includes(query.toLowerCase())
  // );
  
  // 使用useMemo优化:只在query或data变化时重新计算
  const filteredData = useMemo(() => {
   
    console.log('重新计算过滤结果');
    return data.filter(item => 
      item.name.toLowerCase().includes(query.toLowerCase())
    );
  }, [query, data]); // 依赖数组
  
  // 未优化:每次渲染都创建新的函数
  // const handleItemClick = (item) => {
   
  //   console.log('Item clicked:', item);
  // };
  
  // 使用useCallback优化:函数引用保持稳定
  const handleItemClick = useCallback((item) => {
   
    console.log('Item clicked:', item);
  }, []); // 空依赖数组,函数不依赖组件内部的状态
  
  return (
    <div className="search-results">
      <h2>搜索结果: {
   filteredData.length}</h2>
      <ul>
        {
   filteredData.map(item => (
          <ResultItem 
            key={
   item.id} 
            item={
   item} 
            onClick={
   handleItemClick} 
          />
        ))}
      </ul>
    </div>
  );
}

// 使用memo优化的子组件
const ResultItem = React.memo(function ResultItem({
     item, onClick }) {
   
  console.log('ResultItem render:', item.id);
  
  return (
    <li 
      className="result-item"
      onClick={
   () => onClick(item)}
    >
      {
   item.name}
    </li>
  );
});

性能对比:

优化手段 大数据集(10,000项)查询耗时 组件重渲染次数 内存占用
未优化 120ms 5,000 基准线
使用useMemo 2ms (首次120ms) 1 -40%
使用useCallback 不适用 10 -25%
两者结合 2ms (首次120ms) 1 -45%

3. 列表渲染优化

在React中渲染大型列表是常见的性能瓶颈,可以通过虚拟化和分页技术优化:

// 使用react-window实现列表虚拟化
import {
    FixedSizeList } from 'react-window';

function VirtualizedList({
     items }) {
   
  // 行渲染器
  const Row = ({
     index, style }) => (
    <div style={
   {
    ...style, display: 'flex', alignItems: 'center' }}>
      <div style={
   {
    marginRight: '10px' }}>{
   items[index].id}</div>
      <div>{
   items[index].name}</div>
    </div>
  );

  return (
    <div className="list-container">
      <FixedSizeList
        height={
   500}
        width="100%"
        itemCount={
   items.length}
        itemSize={
   50} // 每项高度
      >
        {
   Row}
      </FixedSizeList>
    </div>
  );
}

// 使用自定义虚拟列表实现(简化版)
function CustomVirtualList({
     items }) {
   
  const [scrollTop, setScrollTop] = useState(0);
  const containerRef = useRef(null);
  
  const itemHeight = 50; // 每项高度
  const windowHeight = 500; // 可视区域高度
  const overscan = 5; // 额外渲染项数
  
  // 处理滚动事件
  const handleScroll = () => {
   
    if (containerRef.current) {
   
      setScrollTop(containerRef.current.scrollTop);
    }
  };
  
  // 计算可见区域
  const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
  const endIndex = Math.min(
    items.length - 1,
    Math.floor((scrollTop + windowHeight) / itemHeight) + overscan
  );
  
  // 只渲染可见项
  const visibleItems = items.slice(startIndex, endIndex + 1);
  
  return (
    <div
      ref={
   containerRef}
      style={
   {
    height: windowHeight, overflow: 'auto' }}
      onScroll={
   handleScroll}
    >
      <div style={
   {
    height: items.length * itemHeight }}>
        {
   visibleItems.map(item => (
          <div
            key={
   item.id}
            style={
   {
   
              position: 'absolute',
              top: item.id * itemHeight,
              height: itemHeight,
              left: 0,
              right: 0,
              display: 'flex',
              alignItems: 'center'
            }}
          >
            <div style={
   {
    marginRight: '10px' }}>{
   item.id}</div>
            <div>{
   item.name}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

性能对比:

列表实现 渲染10,000项列表时间 内存占用 滚动帧率
标准React列表 850ms 100% 15 FPS
react-window虚拟化 25ms 15% 60 FPS
自定义虚拟化 30ms 18% 58 FPS

4. React Context优化

Context API提供了便捷的状态共享机制,但使用不当会导致大范围重渲染:

// 未优化的Context使用方式
const ThemeContext = React.createContext();

function App() {
   
  const [theme, setTheme] = useState('light');
  const [user, setUser] = useState({
    name: 'User' });
  
  // 每次App重新渲染时,这个对象都会重新创建
  const value = {
    theme, user };
  
  return (
    <ThemeContext.Provider value={
   value}>
      <Header />
      <Content />
      <Footer />
    </ThemeContext.Provider>
  );
}

// 分离Context优化
const ThemeContext = React.createContext();
const UserContext = React.createContext();

function App() {
   
  const [theme, setTheme] = useState('light');
  const [user, setUser] = useState({
    name: 'User' });
  
  return (
    <ThemeContext.Provider value={
   theme}>
      <UserContext.Provider value={
   user}>
        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2377381.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简单图像自适应亮度对比度调整

一、背景介绍 继续在刷对比度调整相关算法&#xff0c;偶然间发现了这个简单的亮度/对比度自适应调整算法&#xff0c;做个简单笔记记录。也许后面用得到。 二、自适应亮度调整 1、基本原理 方法来自论文:Adaptive Local Tone Mapping Based on Retinex for High Dynamic Ran…

深入理解二叉树:遍历、存储与算法实现

在之前的博客系列中&#xff0c;我们系统地探讨了多种线性表数据结构&#xff0c;包括顺序表、栈和队列等经典结构&#xff0c;并通过代码实现了它们的核心功能。从今天开始&#xff0c;我们将开启一个全新的数据结构篇章——树结构。与之前讨论的线性结构不同&#xff0c;树形…

【Win32 API】 lstrcmpA()

作用 比较两个字符字符串&#xff08;比较区分大小写&#xff09;。 lstrcmp 函数通过从第一个字符开始检查&#xff0c;若相等&#xff0c;则检查下一个&#xff0c;直到找到不相等或到达字符串的末尾。 函数 int lstrcmpA(LPCSTR lpString1, LPCSTR lpString2); 参数 lpStr…

(C语言)超市管理系统 (正式版)(指针)(数据结构)(清屏操作)(文件读写)

目录 前言&#xff1a; 源代码&#xff1a; product.h product.c fileio.h fileio.c main.c 代码解析&#xff1a; 一、程序结构概述 二、product.c 函数详解 1. 初始化商品列表 Init_products 2. 添加商品 add_product 3. 显示商品 display_products 4. 修改商品 mo…

NAT转换和ICMP

NAT nat原理示意 nat实现 ICMP ICMP支持主机或路由器&#xff1a; 差错或异常报告网络探寻 2类icmp报文&#xff1a; 差错报告报文&#xff08;5种&#xff09; 目的不可达源抑制--拥塞控制超时&超期--TTL超时参数问题--问题报文丢弃重定向--不应该由这个路由器转发&a…

【专利信息服务平台-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

BUUCTF——web刷题第一页题解

共31题&#xff0c;admin那题没有&#xff0c;因为环境问题&#xff0c;我做的非常卡 目录 极客大挑战 2019]Havefun [HCTF 2018]WarmU [ACTF2020 新生赛]Include [ACTF2020 新生赛]Exec [GXYCTF2019]Ping Ping Ping [SUCTF 2019]EasySQL [极客大挑战 2019]LoveSQL [极…

哪个品牌的智能对讲机好用?推荐1款,能扛事更智能

在专业通信领域&#xff0c;智能对讲机早已突破传统设备的局限&#xff0c;成为集通信、调度、数据传输于一体的智能化终端。面对复杂多变的作业环境&#xff0c;用户对设备的稳定性、通信效率和智能化水平提出了更高要求。但是&#xff0c;市面上产品同质化严重&#xff0c;部…

【Win32 API】 lstrcpyA()

作用 将字符串复制到指定的字符串缓冲区。 函数 LPSTR lstrcpyA(LPSTR lpString1, LPCSTR lpString2); 参数 lpString1 类型&#xff1a;LPTSTR 一个缓冲区&#xff0c;用于接收由 lpString2 参数指向的字符串的内容。 缓冲区必须足够大才能包含字符串&#xff0c;包括终止…

Vue3——Watch侦听器

目录 手动指定监听对象 侦听ref对象 侦听ref对象中的某个属性 reactive写法 watchEffect 自动侦听 多源侦听 一次性侦听器 watch 是⼀个⽤于观察和响应Vue响应式系统中数据变化的⽅法。它允许你指定⼀个数据源&#xff08;可以是 响应式引⽤、计算属性、组件的属性等&#xf…

Go的单测gomock及覆盖率命令

安装gomock&#xff1a; go get github.com/golang/mock/gomockgo get github.com/golang/mock/mockgen 使用 mockgen 生成 mock 代码: 参考 mockgen -sourceservice/user.go -destinationservice/mocks/mock_user_service.go -packagemocks go test -coverprofilecoverage.out…

Leetcode209做题笔记

力扣209 题目分析&#xff1a;想象一个窗口遍历着这个数组&#xff0c;不断扩大右边界&#xff0c;让r。往窗口中添加数字&#xff1a; 此时我们找到了这个窗口&#xff0c;它的和满足了大于等于target的条件&#xff0c;题目让我求最短的&#xff0c;那么我们就尝试来缩短它&…

Suna: 开源多面手 AI 代理

GitHub&#xff1a;GitHub - kortix-ai/suna: Suna - Open Source Generalist AI Agent 更多AI开源软件&#xff1a;发现分享好用的AI工具、AI开源软件、AI模型、AI变现 - 小众AI Suna 是一个完全开源的 AI 助手&#xff0c;可帮助您轻松完成实际任务。通过自然对话&#xff0c…

25-05-16计算机网络学习笔记Day1

深入剖析计算机网络&#xff1a;今日学习笔记总结 本系列博客源自作者在大二期末复习计算机网络时所记录笔记&#xff0c;看的视频资料是B站湖科大教书匠的计算机网络微课堂&#xff0c;每篇博客结尾附书写笔记(字丑见谅哈哈) 视频链接地址 一、计算机网络基础概念 &#xf…

12 web 自动化之基于关键字+数据驱动-反射自动化框架搭建

文章目录 一、如何实现一条用例&#xff0c;实现覆盖所有用例的测试1、结合数据驱动&#xff1a;编辑一条用例&#xff0c;外部导入数据实现循环测试2、用例体&#xff1a;实现不同用例的操作步骤对应的断言 二、实战1、项目路径总览2、common 文件夹下的代码文件3、keywords 文…

动态IP赋能业务增效:技术解构与实战应用指南

在数字化转型加速的今天&#xff0c;IP地址作为网络通信的基础设施&#xff0c;其技术特性正深刻影响着企业业务架构的效率与安全性。动态IP&#xff08;Dynamic IP&#xff09;作为互联网资源分配的核心机制&#xff0c;早已突破传统认知中的"临时地址"定位&#xf…

【Java ee初阶】http(1)

HTTP 全称为“超文本传输协议”&#xff0c;由名字可知&#xff0c;这是一个基于文本格式的协议&#xff0c;而TCP&#xff0c;UDP&#xff0c;以太网&#xff0c;IP...都是基于二进制格式的协议。 如何区别该协议是基于哪种格式的协议&#xff1f; 形如这种协议格式&#xf…

day18-数据结构引言

一、 概述 数据结构&#xff1a;相互之间存在一种或多种特定关系的数据元素的集合。 1.1 特定关系&#xff1a; 1. 逻辑结构 2.物理结构&#xff08;在内存当中的存储关系&#xff09; 逻辑结构物理结构集合&#xff0c;所有数据在同一个集合中&#xff0c;关系平等顺…

我开源了一个免费在线工具!UIED Tools

UIED Tools - 免费在线工具集合 最近更新&#xff1a;修改了文档说明&#xff0c;优化了项目结构介绍 这是设计师转开发的第一个开源项目&#xff0c;bug和代码规范可能有些欠缺。 这是一个功能丰富的免费在线工具集合网站&#xff0c;集成了多种实用工具&#xff0c;包括 AI …

什么时候可以开始学习深度学习?

咱们先来聊聊机器学习和深度学习的关系~ 这个问题其实挺常见的&#xff0c;之前我也跟不少同事、同学聊过。最近有好几个同学也聊过。 简单说&#xff0c;深度学习是机器学习的一个子集&#xff0c;两者不是并列关系&#xff0c;而是“包含”关系。 你可以这么理解&#xff…