react 性能提升
使用React框架时,可以从两方面提高应用的性能:
- 减少不必要的Render函数的调用。
- 复用Dom
减少不必要的Render函数的调用
在React的生命周期里有四种方式可以触发一个组件render函数的调用,分别是:
1. 组件初始挂载时
2. props 更新时
3. 调用 setState()
4. 调用forceUpdate()
这四种方式中,初始挂载的调用是必不可少的,setState() 时的调用React 已经做了优化,会合并多次处理,forceUpdate()时的调用是应用方主导发起,只有props的更新导致的调用并不是全部有必要的,例如父组件重新渲染,但是子组件的props并未变化,也会触发子组件的render函数调用。
对于statefull的class 类型的组件
可以从两个方法优化:
1. 使用PureComponent取代Component,因为PureComponent 重新定义了shouldComponentUpdate 钩子函数,会对newProps和Props 的属性作浅比较,属性都没有变化时,会返回false,不会再调用render函数,组件的更新流程终止。而Component内定义的shouldComponentUpdate默认是永远返回true。
2.组件内重写shouldComponentUpdate钩子函数,比较newProps和Props,只有属性值发生变化,才返回true。
为了降低newProps 和Props的diff 成本,对props的属性要使用imutable objects,这样可以直接浅比较,就能识别属性有无变化,不需要进行深层次的比较,减少diff 时间复杂度。这就要求父组件在修改state时,不要直接修改Object 和 Array类型数据的值,而是生成新的地址。
例如:父组件的dataList 要增加一个元素“text”,如果直接用push方法,dataList 地址不会变化,子组件的shouldComponentUpdate函数对newProps和props进行浅比较是发现不了变化的。
const {dataList} = this.state; dataList.push("test"); // dataList 是 mutable的,浅比较无法识别变化 this.setState({dataList})
将dataList改为immutable的,使用concat 生成新的dataList。
const {dataList} = this.state; const newDataList = dataList.contat(["test"]); // dataList 是 immutable的,创建新的数组,浅比较可直接判定出变化 this.setState({dataList:newDataList})
对于stateless的function component
class类型的组件可以通过比较newProps和props来减少不必要的渲染,那对于函数式组件,有没有类似的方式呢?有的,React 也设计了一个memo方法,实现相同的功能。官网对React.memo的解释如下:
“React.memo is a higher order component. It’s similar to React.PureComponent but for function components instead of classes.”
React.memo 返回的是一个高阶组件,他的功能和React.PureComponent 一致,减少组件因为不必要的props变化引起的渲染。只是React.memo是作用在函数式组件上的。
memo方法接受两个参数,第一个参数是组件函数,第二个参数是props diff 函数。memo内部默认会浅比较newProps和Props的属性,发生变化时,才执行组件重渲染。我们也可以用第二个参数改变diff方法。
复用Dom
react 在Dom的复用上有两个判定条件:
1. type 相同
2. key 值相同
为减少Dom的创建,在更新时,尽量保持节点的type不变,例如:total属性大于2时,节点由div 变为 span,这样会引起不必要的Dom创建。
function Counter({total}){ return total > 2? <div>计数达到上线</div>: <span>计数没有达到上线<span> }
为减少Dom不必要的创建和更新,要合理的使用key。对于单节点,不要滥用key(特殊:对于逻辑复杂的组件,更新成本高于创建时,在单节点上使用key,也可以提高性能)。 对于数组节点,key值要保持唯一且稳定。