React - 渲染性能优化
总结避免子组件不必要渲染的一些方法。
水文预警:看题目就知道,这篇是水文,写给自己的备忘录。
在 React
中,父组件一旦渲染,子组件也会跟着重新渲染。(父债子还)
试想在复杂业务场景下,一群子组件依赖于父组件,然后父组件有逻辑触发了渲染,一群子组件跟着渲染,这不是炸了。(杀鸡用牛刀)
所以 React
性能优化的要素就是打断父子组件之间的强依赖关系,跳过不必要的重新渲染。但是当属性更新时,子组件又能进行及时响应并渲染。(指哪打哪)
本文讨论在子组件控制重新渲染的一些方法。
shouldComponentUpdate
shouldComponentUpdate
是一个生命周期,在组件更新阶段中触发,用于判断组件是否需要渲染。
组件更新:componentWillReceiveProps(接收属性) -> shouldComponentUpdate(判断是否渲染) -> componentWillUpdate -> render(重新渲染) -> componentDidUpdate
|
该生命周期方法接收两个变量,表示下一次渲染后的属性和变量;返回一个布尔值表示是否需要渲染。
在方法中我们可以对 nextProps(更新后属性)
、 this.props (当前属性)
、 nextState(更新后状态)
、 this.state (当前状态)
进行一些判断,来决定是否需要重新渲染。当父组件传给子组件的属性没有发生变化时,子组件自然就不用重新渲染了,就达到了性能优化的效果。
但是需要注意的是,深比较(递归遍历对象下面的所有属性)可能得不偿失,一套比较下来还要渲染,还不如直接渲染来的快。
所以正确的食用方法如下:
- 只进行浅比较,对比引用对象即可:
nextProps === this.props
- 进行深层次控制,比如只对比其中一部分属性:
nextProps.username === this.props.username
- 将对象转化为
immutable
对象进行比较。但是js
转immutable
再转回js
太麻烦了
immutable.js
是不可变数据集合,数据一旦创建就不能被修改,可以进行高效的惰性比较。
PureComponent
每个子组件都写一通 shouldComponentUpdate
也太麻烦了,所以 React
为我们准备了 PureComponent
组件,原型对象中默认实现了 浅比较 。
By the way,若是在 PureComponent
使用了 shouldComponentUpdate
,该方法会被重写。
|
上述两种方式只适用于 对象式组件,对于 函数式组件 需要使用以下方法:
memo
React.memo()
是一个 HOC(高阶组件),传入两个参数:函数式组件、判断方法,返回被包裹后的对象。通过判断方法中返回的结果来决定是否需要重新渲染。
第二个参数也可以不传,默认就是浅比较。
|
使用 Hooks
特性后,React.memo()
方法就失效了。因为每次渲染返回的都是一个新闭包,不管怎么比较都是重新渲染。
贴心的 React
团队提供了以下两个 Hooks
方法:
useMemo
useMemo
接受两个参数:函数 和 依赖项数组,返回缓存 memoized
值(个人理解是包装后的对象)。只有当依赖性数组中的某个值发生变化时,该函数才会重新渲染。
useMemo
的控制粒度更细,控制的是传入的函数,而不是整个组件。
|
useCallback
useCallback
与 useMemo
类似,也接受两个参数:回调函数 和 依赖项数组,返回缓存 memoized
值。当依赖性数组中的某个值发生变化时,传入的回调函数被新的闭包函数取代。
说白了就是,useMemo
中的函数直接运行,useCallback
返回的函数需要你手动跑。类比于 call
和 bind
。
|
总结
上文只是对这些 API 过了一遍,还是乱讲的那种。不过产生了一些体会,只可意会不可言传。
从 生命周期 到 Hooks,从 对象式组件 到 函数式组件,从 命令式 到 声明式,应该是思想上转变,以及为什么要引入 Hooks
。
而不是讨论 useEffect
中传什么参数可以模拟 componentDidMount
还是 componentWillUnmount
还是 componentWillUpdate
。
- 本文作者:zhaoo
- 本文链接:https://www.izhaoo.com/2020/08/01/react-render-optimization/index.html
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!