一.React更新机制
1. react的渲染流程:
(1 ES6的JSX语法会通过babel转换成React.createElement()函数的调用并生成ReactElement对象 组成 对象树(js对象 即 虚拟DOM);
(2 通过ReactDOM.render让 虚拟DOM 和 真实DOM同步起来(将虚拟DOM渲染成真实DOM),这个过程中叫做协调(Reconciliation);
采用虚拟DOM,而不是直接修改真实DOM的原因:
(1) 很难跟踪状态发生的改变;
(2) 操作DOM性能较低,传统的开发模式会进行频繁的DOM操作, 会引起浏览器的回流和重绘, 虚拟DOM可以对多次操作进行合并;
(3) 虚拟DOM帮助我们从命令式编程转到了声明式编程的模式(我们开发者只需要告诉React希望UI是什么状态,React来确保DOM和这些状态是匹配的,开发者就可以从手动更改DOM,属性操作,事件处理中解放出来)
2. react 的更新流程:
react-diff算法过程:
(1) 同层节点之间进行相互比较,不会跨层级比较;
(2) 情况一: 同一层级 不同类型节点,React会拆卸掉对应的旧的整个节点树,并建立起新的节点树; 销毁时会触发componentWillUnmount(),新建和插入时触发componentWillMount() 方法, 紧接着 componentDidMount() 方法;
如下:
(3) 情况二:对比同一类型的元素, React会保留DOM节点,仅比对及更新有改变的属性,例如className,style…
(4) 情况三:同一类型的组件元素,组件会保持不变;
- react会更新该组件的props,并调用componentWillReceiveProps() 和 componentWillUpdate() 方 法;
- 下一步,调用render()方法,diff算法将在之前的结果以及新的结果中进行递归;
(5) 对子节点进行递归 , 及列表遍历过程;
- 当递归DOM节点的子元素时,React会同时遍历两个子元素列表,当产生差异时,生成一个mutation;
在最后插入一条数据情况: 前面两个比较完全相同,不会产生mutation;最后一个会产生,将其插入到新的DOM树中即可;
在最上面插入一条数据情况:React会对每个子元素产生一个mutation,而不是保持原有的元素不变,因为diff过程是从第一个开始逐个进行比较的,所以每个元素相当于都是新的;
这种方式会带来一定的性能问题,因此key的优化出现了;
二.key的优化
上面列表数据遍历插入方式不同对key的需求也就不同了;
方式一:在最后位置插入数据, 有无key对于优化的意义不大;
方式二:在前面插入数据,
没有key的情况下,所有的元素都需要进行修改;
有key时, React使用key来匹配 原有树上的子元素以及最新树上的子元素,旧的元素仅仅进行移位,不需要进行任何的修改;
key的注意事项:
(1) key 应该是唯一的;
(2) key 不要使用随机数(随机数在下一次render时,会重新生成一个数字,失去优化的意义);
(3) 使用index作为key,对性能时没有优化的;
三.组件嵌套的render调用
React中,使用setState修改数据后,就会重新的调用组件的render函数, 在多个组件嵌套的情况下,父组件(APP)中的数据修改并不影响子组件时,所有子组件也会重新调用render函数重新渲染,性能必然是很低的;
如何控制render方法是否被调用?
一.针对类组件
(1) shouldComponentUpdate() 生命周期函数(SCU);
1. 该方法参数:
- 参数一: nextProps 修改之后,最新的props属性
- 参数二: nextState 修改之后,最新的state属性
2. 该方法返回值是一个Boolean类型:
- 返回true, 会调用render方法;
- 返回false, 不会调用render方法;
- 默认返回的是true,也就是只要state发生改变就会调用render;
(2) PureComponent
如果所有的类组件,我们都需要手动来实现SCU,会给开发者增加非常多的工作量;
SCU使用的目的是什么?props或state中的数据是否发生了变化,来决定SCU返回true或false,最终决定是否更新当前的类组件;
事实上,react已经考虑到这一点,所以react已经帮我们实现好了,如何实现?
shallowEqual方法: 这个方法中,调用 !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState),这个shallowEqual就是进行浅层比较:
如何使用?
将Component 替换成 PureComponent来创建类组件:
二.针对函数式组件
(1) 高阶组件memo
如何实现?
如何使用?