设计动力
- 在某一时间节点调用 React 的 render() 方法,会创建一棵由 React 元素组成的树。在下一次 state 或 props 更新时,相同的 render() 方法会返回一棵不同的树。React 需要基于这两棵树之间的差别来判断如何有效率的更新 UI 以保证当前 UI 与最新的树保持同步。
Diffing 算法
首先:比较两棵树的根节点
- 对比不同类型的元素
-
- 当根节点为不同类型的元素时,React 会拆卸原有的树并且建立起新的树
-
-
- 例如:当一个元素从< a > 变成 < img >,从 < Article > 变成 < Comment >,或从 < Button > 变成 < div > 都会触发一个完整的重建流程
-
-
- 当拆卸一棵树时,对应的 DOM 节点也会被销毁
-
-
- 执行 componentWillUnmount()
-
-
-
- 所有跟之前的树所关联的 state 也会被销毁,在根节点以下的组件也会被卸载,它们的状态会被销毁
-
-
- 当建立一棵新的树时,对应的 DOM 节点会被创建以及插入到 DOM 中
-
-
- 执行 componentWillMount() 方法,紧接着 componentDidMount() 方法
<div> <Counter /> </div> <span> <Counter /> </span> //React 会销毁 Counter 组件并且重新装载一个新的组件
-
- 比对同一类型的元素
-
- 当比对两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性
//例如: <div className="before" title="stuff" /> <div className="after" title="stuff" /> // 通过比对这两个元素,React 知道只需要修改 DOM 元素上的 className 属性 <div style={{color: 'red', fontWeight: 'bold'}} /> <div style={{color: 'green', fontWeight: 'bold'}} /> //通过比对这两个元素,React 知道只需要修改 DOM 元素上的 color 样式,无需修改 fontWeight ```
- 当比对两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性
-
- 在处理完当前节点之后,React 继续对子节点进行递归
- 比对同类型的组件元素
-
- 当一个组件更新时,组件实例保持不变,这样 state 在跨越不同的渲染时保持一致。React 将更新该组件实例的 props 以跟最新的元素保持一致,并且调用该实例的 componentWillReceiveProps() 和 componentWillUpdate() 方法
-
- 下一步,调用 render() 方法,diff 算法将在之前的结果以及新的结果中进行递归
- 对子节点进行递归
-
- 默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。
-
-
- 在子元素列表末尾新增元素时,更变开销比较小
-
- Keys
-
- 当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。
<ul> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul> <ul> <li key="2014">Connecticut</li> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul> // React 知道只有带着 '2014' key 的元素是新元素,带着 '2015' 以及 '2016' key 的元素仅仅移动了
- 当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。
-
- key 不需要全局唯一,但在列表中需要保持唯一
-
- 不建议用数组下标作为key;因为一旦有顺序修改,diff 就会变得慢