react 的 reconciliation 主要描述了在实现 React 的 “diffing” 算法过程中所作出的设计决策,以保证组件更新可预测,且在繁杂业务场景下依然保持应用的高性能。

总结(针对有效利用diffing算法优化自己的代码)

  1. 尽量避免节点元素的变更(e.g. <p> -> <div>
  2. 节点元素属性变更最小化(p.s. 能只改一个属性,就不改两个属性)
  3. 列表节点设置唯一key,key保证在页面生命周期中的唯一性和不变性(p.s. 针对循环列表尽量避免使用数组的key,在存在新增和删除时,单一元素在数组中的key是变化的)

Diffing 算法

当对比两棵树时,React 首先比较两棵树的根节点。不同类型的根节点元素会有不同的形态。

对比不同类型的元素

当节点的元素变更会导致,当前节点会触发一个完整的销毁重建流程(即:当前节点及其子节点均会被销毁之后重新渲染,不会触发对比更新,也就是子节点已渲染内容不会被重新利用)。应尽量避免根节点(或者高级节点)的元素变更(比如 <span> -> <div>

总结:应尽量避免节点元素的变更,造成更大的消耗

对比同一类型的元素

react 回比对属性的变更,仅更新已变更属性

style样式也会一一对比之后实现更新最小化

总结:属性变更应保证变更最小化

对比同类型的组件元素(?)

当一个组件更新时,组件实例会保持不变,因此可以在不同的渲染时保持 state 一致。React 将更新该组件实例的 props 以保证与最新的元素保持一致,并且调用该实例的 UNSAFE_componentWillReceiveProps()UNSAFE_componentWillUpdate() 以及 componentDidUpdate() 方法。

下一步,调用 render() 方法,diff 算法将在之前的结果以及新的结果中进行递归。

对子节点进行递归

节点的更新满足以上条件。

默认情况下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。

针对子节点的新增删除,如果新的子节点插在子节点列表末尾,消耗最小(React会匹配前边所有的节点被有效利用,只是单纯添加新的节点到末尾);如果新的节点加在子节点列表开头,消耗最大(React 判断新旧树第一个元素不一致,便会销毁之后所有的子节点然后再重建,此时的消耗会是重新渲染所有的子节点)

Keys

解决以上问题的方法就是利用Keys;配置Keys之后,React会通过Key去匹配新旧节点,保证已渲染节点的有效利用

注意:针对于循环生成的子节点,单纯通过数组的key(即 数据所在数组中的位置)设置的key,对于新增和删除操作并不会起到有效作用,需要保证在新增和删除时节点的key的唯一性