参考文档https://reactjs.org/docs/reconciliation.html
传统的diff算法https://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf。为了用最少的操作,把一颗树结构转化成另外一颗树结构,使用了非常复杂的算法,复杂度达到了O(n^3),其中n为树的节点数。
与传统的diff算法相比,React使用的是一种复杂度为O(n)的diff算法,基于两点基本的假设:
1.Two elements of different types will produce different trees.
2.The developer can hint at which child elements may be stable across different renders with a key prop.
不同类型的DOM元素
<div>
<ComponentA />
</div>
----------------------------------
<span>
<ComponentA />
</span>
由于div和span是不同类型的DOM元素,React将重新渲染,原来的树结构将会被销毁,ComponentA也将被卸载,componentWillUnmount会被调用。新的ComponentA生成后,componentWillMount和componentDidMount将会被调用。
相同类型的DOM元素
<div className="class1">aaaa</div>
-------------------------------------------
<div className="class2">aaaa</div>
React不会销毁原来的DOM元素,仅仅修改className。
相同的Component
<ComponentA a={1} />
---------------------------------
<ComponentA a={2} />
React不会销毁ComponentA,componentWillReceiveProps 和componentWillUpdate将会被调用,之后render会被调用,diff算法将会进行递归比较原先的和当前的结果。
对于children的比较
默认情况下,React将会按顺序逐一与原先的children进行比较。
<ul>
<li>first</li>
<li>second</li>
</ul>
------------------------------------------
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
对于上面的情况,React会直接在ul的末尾插入
<li>third</li>
而不是重建整个ul的子节点。
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
-----------------------------------------
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
对于上面的情况,由于React是按顺序逐一比较的,React会认为所有的子节点都发生了变化,整个ul的子节点都会被重建。
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>
若使用了key,React会比较子元素的key值是否在原先的元素中存在过,若存在过,则该元素不会发生重建。对于上述情况,React只会新建一个元素:
<li key="2014">Connecticut</li>
因此我们在根据数组渲染元素的时候,最好不要把index设成key,当数组元素之间发生了交换,或者在数组头部或中间插入元素时,将会发生意想不到的现象。