React Reconciliation
一.什么是Reconciliation
- 每次
render
的时候,React
都会产生一棵由React元素
组成的树,这个树形结构就是所谓的虚拟DOM
,下次render
的时候又会产生新的一颗树,对比这两棵树的不同的过程,就是调和,即Reconciliation
二.对比过程,分为以下几种情况
1. 根节点类型不同的情况
- 如果根节点就不同,如下的
div
变成了span
,那么对比的开销太大了,所以React
选择了重建,会将原有的树卸载
,再将新的树装载
<div>
<Counter />
</div>
<span>
<Counter />
</span>
2.根节点类型相同的情况
如果是根节点相同的情况下,只会更改有变化的部分
当节点类型是HTML元素类型时(如
span,p,div
等)
如下,只会更改color
变化的值和div
里变化的值
<div style={{color: 'red', fontWeight: 'bold'}}>hello world</div>
<div style={{color: 'green', fontWeight: 'bold'}}>good bye</div>
-
当节点类型是React组件类型时
如下,
React
并不知道如何去更新DOM,因为这些逻辑还在React
组件当中,所以只能去更新props
的值,引发这个组件的更新过程,实现对子元素的递归<Todo text="old"> <Todo text="new">
3.多个同级元素的情况
- 有时候我们会遇到增加同级元素的情况,这里以HTML元素为例,
React
组件也是一样 - 如果是在最下方插入的情况,那么事情没那么糟糕,因为
React
会去对比每个元素,first
对比fisrt
,second
对比second
,然后发现多了一个third
,那么就在原有的基础上加上这个元素 - 但是如果是在最上方插入的情况,
React
使用的O(n)
复杂度的算法会逐个对比,即first
对比third
,second
对比first
,然后发现多了一个second
,这样的话,本来只是新增一个元素的情况,会变成全部元素都要修改
//最下方插入
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
//最上方插入
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>third</li>
<li>first</li>
<li>second</li>
</ul>
- 有没有解决上面最上方插入所带来的问题的方法呢,答案是使用唯一的
Key
- 当使用了唯一的
Key
以后,React
会知道,first
和second
是原来的那个元素,现在新增的只是third
元素,当然这里有很重要的一点,这个Key
必须是稳定不变的,试想一下,如果原先first
是1,second
是2,后面third
是1,first
是2,second
是3,这样的话,从上到下一一对比,每个元素又要重新渲染,就失去了Key
的意义,导致不必要的元素重新创建和子组件中的状态丢失【1】
//最上方插入
<ul>
<li key={1}>first</li>
<li key={2}>second</li>
</ul>
<ul>
<li key={0}>third</li>
<li key={1}>first</li>
<li key={2}>second</li>
</ul>
三.代码优化思考
1.以前早就听说过不要用数组下标作为Key
,但是现在才对Key
值需要唯一且稳定有了深刻理解,具体理解见上面的【1】,数组下标作为Key
也不是一棒子打死,当这些元素不需要重新排序的时候,可以采用,当这个元素需要重新排序的时候,就不要用数组下标了
2.也不要用Math.random()
作为Key
3..针对React
对根节点类型不同的情况的处理,在写代码的时候就要避免改变根节点的情况