react 性能优化
使用 shouldComponentUpdate
react 生命周期
从 react 的生命周期可以看出,当 component 的 props 和 state 改变时,会调用 shouldComponentUpdate 确认要不要刷新。默认 shouldComponentUpdate 都是返回 true ,这样 component 就会 re-render 。我们可以重写 shouldComponentUpdate , 使之只在特定情况返回 true ,其他情况返回 false ,来避免不必要的 re-render 。
例子
比如组件的改变只依赖 props.color 或 state.count ,可以像下面这样写:
1 | class CounterButton extends React.Component { |
上面例子中,我们在 shouldComponentUpdate 中判断 props.color 和 state.count 前后有没变化,有就返回 true , 没有就返回 false ,避免了不必要的重新渲染。
可是当依赖的属性很多时,手写 shouldComponentUpdate 就会变得很麻烦。幸运的是 react 给我们提供了偷懒的工具—— React.PureComponent 。当组件继承自 React.PureComponent 时,内部会自动帮我们检查 props 和 state 的所有字段前后有没发生变化,并决定要不要重新渲染,所以大部分情况不用再重写 shouldComponentUpdate 。
现在代码变成下面这样:
1 | class CounterButton extends React.PureComponent { |
属性内部保持不变性
使用 React.PureComponent 要注意的一点是在比较对象(object、array)时只比较引用,不对内部进行对比。看下面例子:
1 | class ListOfWords extends React.PureComponent { |
在 handleClick 使用 push , this.state.words 的内部发生变化,引用并没有变化,所以 setState 后 ListWords 不会刷新。解决办法如下:
1 | // 办法1 |
上面代码都会生成一个新的 words 数组对象,这样 ListOfWords 就能比较出 props.words 前后发生变化,进行重新渲染。
保持不可变(Immutability)有以下好处:
- 我们可以对每个时间点的
props和state进行快照保存,这样就可以返回任意时间点的状态。在某些场景这非常有用,比如游戏中存档,文档编辑的 undo 和 redo。 - 让检测对象变化变得简单。对于可变对象的变化检测非常麻烦,要检测对象里的所有字段。而不可变对象的检测就很简单,只要检测前后引用是否发生变化。这点对于创建
React.PureComponent是非常有用的。
Function Components
如果一个组件只有 render 方法,且没有 state ,那么可以使用 Function Components 。直接使用一个函数,入参为 props ,返回要渲染的内容。比如:
1 | function Square(props) { |
总结
react 的性能优化可以从四方面入手:
- 使用
shouldComponentUpdate避免不必要重新渲染 - 如果对于相同的
props和state,render返回的结果一样,那么就继承React.PureComponent,避免重新渲染。 - 保持对象的不可变性(
Immutability),减少对象比较的消耗。 - 对于只有
render方法,且没有state的组件使用Function Components,减少一层封装。