跳到主要内容

性能优化

性能优化,从整个项目来看,其实一般首先通过监控埋点来进行分析针对性优化

React 项目中有哪些细节可以优化?实际开发中都做过哪些性能优化​

实际项目中开发过程中还有很多其他的优化点:

通过React DevTools分析​

  • React DevTools是一个强大的开发工具,可以通过监控组件渲染和性能数据

通过设置,在操作页面的时候,可以看到哪些组件发生了更新渲染

  • 可以很清楚的看到组件是否有重复渲染,可以避免大量不必要的重新渲染。

通过Webpack Bundle Analyzer分析​

  • 通过Webpack Bundle Analyzer工具,我们可以可视化分析打包后的bundle大小
  • 针对性的对代码块、各依赖模块大小进行按需优化

vite对应 rollup-plugin-visualizer

shouldComponentUpdate/PureComponent​

  • 对于类组件的话,可以使用shouldComponentUpdate/PureComponent
  • React 组件会根据 shouldComponentUpdate 的返回值,来决定是否执行该方法之后的生命周期, 决定是否对组件进行重新渲染
    • shouldComponentUpdate是组件更新时调用
    • shouldComponentUpdate 的默认值为 true,也就是说“无条件 重渲染
  • PureComponent,会对props 和 state 进行浅比较,“浅比较”将针对值类型数据对比其值是否相等,而针对数组、对象等引用类型的数据则对比其引用是否相等,不相等就重新渲染

不必要的标签,使用React.Fragments​

  • React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点

    • Fragment意思是碎片

    为了解决包裹使用多个元素div进行嵌套,无形中会增加浏览器的渲染压力,在react15之前版本,render函数的返回必须要有一个根节点,否则会报错,通常都是使用div包裹作为根节点, 在react16版本开始,render可以支持返回数组,这一个特性已经可以减少不必要节点的嵌套。。。当然如果你不喜欢用数组的形式,可以使用react16版本推出的Fragment来作为根节点包裹元素,用于减少不必要的嵌套组件

    Fragment与Vue.js的 <template>功能类似,可做不可见的包裹元素

    Frament简写形式 <></>,它看起来像空标签

    1、减少浏览器渲染的压力

    2、减少不必要的节点嵌套

使用keep-alive​

react方案

  • https://github.com/CJY0208/react-router-cache-route

  • umi框架的 : https://github.com/alitajs/umi-plugin-keep-alive

  • 能在组件切换过程中将状态保留在内存中**,**第一次被创建的时候可以缓冲下来, 防止重复渲染DOM

    • 场景:比如,当我们做商城时,商品分类的时候,点击某一个商品分类目录,就执行ajax请求获取对应的商品,我们可以使用keep-alive来进行缓存,防止重复渲染DOM,记录之前点击的某商品分类目录 (记录组件切换时的状态),如果不使用keep-alive,当组件切换时,状态会记录最初时的状态,而不会保留之前的记录状态,则会给用户体验不太友好

React.PureComponent、React.memo​

  • React.PureComponent用于class组件
  • React.memo用于函数式组件

可提高组件性能,防止重复渲染

  • React.memo ,可以缓存函数组件的渲染结果,可以控制函数组件的重新渲染,在组件前后两次 props 对比结果一致的情况下,它会直接复用最近一次渲染的结果,从缓存里获取
  • React.PureComponent: 会检查 上一个 state 和 props 以及下一个 props 和 state 是否相等
  • React.memo(): 只检查props的状态,发生变化则会重新渲染

React.memo​

React.memo是React中的一个高阶组件(Higher-Order Component),用于优化组件的渲染性能。React.memo可以用于包裹函数组件,用于对组件进行浅层的props比较,如果前后props没有变化,则会使用缓存的组件渲染结果。

  • 组件的props没有变化时,可以使用React.memo进行性能优化,避免不必要的重新渲染。
  • 当组件的props是通过父组件传递的,而父组件的渲染频率较高,但子组件的props并不频繁变化时,可以使用React.memo来避免子组件的重复渲染。

需要注意的是,React.memo仅进行浅层的props比较,如果传递给组件的props是复杂的对象或数组,且其内部发生了变化,React.memo可能无法正确地检测到变化

如何解决复杂对象浅层比较无法检测变化?

  • 1、可以使用深层比较函数逻辑,来判断2个对象是否发生变化
    • 通过上一个props和当前props比较
  • 2、可以借助第三方库,如使用 Lodash 的深层比较函数 isEqual
    • 也可以自己手写深层比较函数
jsx
useEffect(() => {
// 在 useEffect 中进行深层比较
if (!deepEqual(prevDataRef.current, data)) {
// 如果数据发生变化,执行相应的逻辑
// 例如:更新状态、发送网络请求、或其他处理
console.log('Data has changed:', data);
}

// 更新前一个 props 的引用
prevDataRef.current = data;
}, [data]);

useCallback/useMemo​

  • 所有 Function Component 内函数事件方法必须用 React.useCallback 包裹,以保证准确性与性能。
    • 如果不用useCallback在每次App重渲染(调用)时, 方法都会被重新创建一次,如果内部有依赖,仅当依赖改变后才会重新创建
  • useCallback 主要用来解决使用渲染的性能问题,可以避免不必要的渲染
    • useCallback 是要和 shouldComponentUpdate/React.memo 配套使用的,才会避免参数不变的情况下的防止重复渲染,提高性能
  • 具有缓存性、它仅会在某个依赖项改变时才重新计算

利用 React.lazy 和 React.Suspense​

  • 利用 React.lazy 和 React.Suspense 延迟加载不是立即需要的组件。

    • 当前不需要的组件不应该加载,我们应该尽量做到按需加载

长列表虚拟滚动渲染​

react-virtualized / react-window