减少重绘和并发执行
实现
- 减少不必要的setState
setState是用来改变数据,触发视图更新的方法,调用setState会触发组件重绘。在复杂组件中setState会触发整个组件包括其子组件的重绘,这个是导致渲染进程被阻塞的主要原因。因此,应尽量细化组件,在必要的子组件中执行setState,减少在复杂组件中setState带来的重绘成本。 - setNativeProps更新属性
用原生属性更新setNativeProps的方法去替代setState。一些样式属性上的切换和变化,可以用setNativeProps去更新,从而减少重绘。 - shouldComponentUpdate减少不必要的重绘
在组件shouldComponentUpdate周期,即重绘render前,判断组件state和props的数据变化,没有变化则不执行重绘。Immutable.js判断复合类型的变化 - PureComponent组件
组件的setState如果只有非对象数据类型的变化,可以用PureComponent,不会根据父组件的重绘而导致自身重绘。 - 减少动画时执行阻塞
组件初始化后,利用InteractionManager使数据请求在动画完成后再执行,减少js线程和掉帧现象,避免切换组件时动画卡顿和延迟,但是同时会延迟了数据请求,会导致再次渲染。 - 提前获取数据,减少渲染
组件渲染慢的问题,像个人中心,好友列表等,在进入页面前先把数据加载完毕,在组件初始化的时候赋值state数据,使组件先获得数据,只渲染一次。(若在组件渲染完之后,再获取数据,会再次渲染)与第5点是矛盾的,可以根据进入导航的交互体验去取舍。
组件生命周期及重绘机制
组件的三种状态及生命周期函数:
- 挂载阶段 Mounting
- componentWillMount:组件挂载之前执行,在render之前调用
- componentDidMount: 组件渲染完成,在所有子组件都render完之后调用
- 组件变化 Updating
- componentWillUpdate:组件将要重新渲染
- componentDidUpdate:组件重新渲染完成
- 卸载阶段 UnMounting
- componentWillUnmount: 卸载组件
两种特殊状态的处理函数
componentWillRecevieProps:组件将要接收新的props时执行
shouldComponentUpdate(nextProps, nextState):判断组件是否应该重新渲染,默认是true
- setState执行时,一般会触发组件视图重绘。
- 前后不改变state值 和 无数据交换的父组件的重渲染 的 setState 都会导致组件的重复渲染
- 组件的功能细分与dom差异化重绘成本有关。
- 重绘步骤
- react重新构建虚拟dom树。
- 与上一个虚拟dom树对比diff,得出dom结构的差异。
- 对发生变化的组件进行重绘。
- 判断setState没有发生变化时,执行return false可阻止不必要的渲染
- Immutable.is() / lodash _.isEqual() 判断复合类型的数据变化
- setState执行时,一般会触发组件视图重绘。
复合类型的数据变化比较
Immutable.is()
实现
1
2
3
4
5
6import { is } from 'immutable';
shouldComponentUpdate: (nextProps = {}, nextState = {}) => {
return !(this.props === nextProps || is(this.props, nextProps)) ||
!(this.state === nextState || is(this.state, nextState));
}性能
Immutable使用Structural Sharing(结构共享)比较的是两个对象的 hashCode 或 valueOf(对于 JavaScript 对象)。由于 immutable 内部使用了 Trie 数据结构来存储,只要两个对象的 hashCode 相等,值就是一样的。- 减少内存
- 避免了深度遍历比较,便于比较复杂数据
lodash _.isEqual()
实现
1
2
3
4
5
6shouldComponentUpdate (nextProps, nextState) {
if (_.isEqual(this.state, nextState) && _.isEqual(this.props, nextProps)) {
return false
}
return true
}lodash _isEqual() 思路
同数据类型
数组 equalArrays
- 数组长度
- set关联
对象 equalObject
- key属性名
- value值
- constructor是否相同
其他类型 equalByTag
- Buffer
- Boolean、Date、Number,通过+value转化为0、1比较
- error 比较error.name && error.message
- regExp、string,转化为string比较
- map、set,转化为array,equalArrays
- 不同类型 -> false
性能
_isEqual 对象层级越深,越耗时
diff算法
- tree diff
- dom tree分层级
- 稳定dom的结构有利于提高新能,应尽量减少dom频繁的移除或添加
component diff
- 同一类型组件
- v-dom变化 tree diff
- v-dom不变,使用shouldComponentUpdate判断是否进行重绘,提高性能
- 不同类型组件 dirty component,替换整个组件下的子节点
- 同一类型组件
element diff
- 插入节点、移动节点、移除节点
- 同一层级的子节点添加唯一key值进行区分,通过key值判断集合中是否存在相同的节点,以此判断对节点的更新:移动或增删,是否可复用元素
- 避免大量节点拖拽排序
触发重绘步骤
减少新建变量
事件绑定this
1 | handleClick = () => { // 属性初始化 |
组件更新优化
- 单个组件更新:避免父节点类型随意更改
- 组件列表,key优化:key是唯一且稳定不变的,避免用数组index作为key
redux性能优化:reselect
实现原理:只要相关的状态不变,就直接用上一次缓存的结果
资料