性能优化
性能优化,从整个项目来看,其实一般首先通过监控埋点来进行分析针对性优化
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方案
-
能在组件切换过程中将状态保留在内存中**,**第一次被创建的时候可以缓冲下来, 防止重复渲染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
- 也可以自己手写深层比较函数
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配套使用的,才会避免参数不变的情况下的防止重复渲染,提高性能
- useCallback 是要和
- 具有缓存性、它仅会在某个依赖项改变时才重新计算
利用 React.lazy 和 React.Suspense
-
利用
React.lazy和React.Suspense延迟加载不是立即需要的组件。- 当前不需要的组件不应该加载,我们应该尽量做到按需加载
长列表虚拟滚动渲染
react-virtualized / react-window
- https://github.com/bvaughn/react-virtualized
- react-window 【react-virtualized作者重写的库】