世界上最伟大的投资就是投资自己的教育
react 和解之 diff 算法
这里主要是概述下react
的和解的一个过程。
目录
diff 算法
当两个树进行对比时,react
会首先比较两个树的根元素,根不同,行为上会有所差异。
不同类型的Elements
每当根元素不同类型时,React
就会拆除旧树并从头开始构建新树。
拆除树时,旧的DOM
节点被删除。组件会接收componentWillUnmount()
方法。
在构建新树时,将新的DOM
节点插入到DOM
中。组件会先接收componentWillMount()
,然后接收componentDidMount()
。任何与旧树相关的状态都会丢失。
如果根组件被移除且构建新的,那么他的子节点也将被移除。例如:
// old
<div>
<Counter />
</div>
// new
<span>
<Counter />
</span>
这两个在比较的时候,发现根元素不同,那么会移除重新构建,这个时候下面的子节点虽然相同,但是仍然会被移除。
同类型的DOM Elements
react
在比较两个相同类型的DOM elements
的时候,他会去比较属性 (attributes
),如果不同,他会保持底层Dom
节点,仅仅改变有过变化的属性。例如:
// example1
<div className="before" title="stuff" />
<div className="after" title="stuff" />
// example2
<div style={{color: 'red', fontWeight: 'bold'}} />
<div style={{color: 'green', fontWeight: 'bold'}} />
example1: 在比较这两个元素时,React
发现元素相同,检查属性,属性不同,只会在底层DOM
节点上修改className
属性.
example2: 这个和example1
差不多,他也是只会检查的style
属性不同,只会改变color
属性,不会改变fontWeight
.
在处理玩
DOM
节点之后,React
会递归子节点进行处理.
相同类型的Component Elements
当组件更新的时候,组件的实例不会改变,便于维持当前组件的状态。React
会更新底层组件实例的props
去匹配新的元素。并且调用实例的componentWillReceiveProps
和componentWillUpdate
事件方法。
然后
render()
方法被调用,并且diff
算法对先前的结果和新的结果进行递归。
递归 Children
当React
在Dom
节点递归子节点的时候,他会默认的迭代比较两个子节点,并且在有差异时发生改变。这是没有问题的,比如:
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
这个树的对比,他会首先对比根元素,根元素相同,他会递归子节点进行对比。这时候两个进行对比,相同继续比较下一个,不同就做出改变。
这个例子在比较子节点的时候,他会匹配两个<li>first</li>
树,匹配两个<li>second</li>
树,比较第三个树时,发现存在差异,就会做出改变,在这里,他会插入<li>third</li>
树。
如果在子节点开始就插入一个树呢?会如何?他会导致性能下降。
如下所示:
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
这时候对比子节点上的树,他会为每个树的差异做出改变。而不是保留存在的<li>Duke</li>
和<li>Villanova</li>
。
所以说,这样做会导致性能的问题。
那该怎么解决呢?继续往下看。
Keys
当然,React
团队知道存在这种隐患,所以他们为子节点提供了key
属性,当存在key
的时候,他就会在新旧的两个之间用key
进行对比。
现在我们添加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>
对比的时候,他会知道元素上的2014
是新的,会去添加。而2015,2016
也存在,只是移动了。
在这里问个问题,他为什么会记得前后的keys
?
-
key
的取值范围- unique
这个唯一,是指在当前的孩子里是唯一的,并不是说全局。
- index
可以用数组的索引,但是前提是不对树进行重新排序 (reorders)。
为什么?因为组件实例是根据key
来重新排序的,如果改变来位置,那么索引值也会改变。
性能建议
react
基于上面的假设进行对比。如何和假设不符合,那么会带来性能上的问题。
- 该算法不会去匹配不同类型组件的子元素,会
re-mount
。所以当你发现你的两个组件有相似的输出的时候,去公用他们成为一个类型。 -
key
的值应该是稳定的,可预测,唯一的。 不稳定的key
会导致组件实例和DOM
节点被不必要的重新创建。甚至有可能丢失组件中的状态。这些对性能是一个挑战。
原文链接
拓展阅读
为什么都已经有原文来,自己还看一遍写出来?
看一遍的印象,是没有翻译一篇文章的印象深刻的。
本站文章均为原创内容,如需转载请注明出处,谢谢。
© 汕尾市求知科技有限公司 | Rails365 Gitlab | 知乎 | b 站 | csdn
粤公网安备 44152102000088号 | 粤ICP备19038915号
Top
多谢大佬
加了 anchor 定位的功能,大佬可以稍微修改下内容重新保存试一下效果
@hfpp2012
额,定位功能咋改,github 上可以定位呀。
https://github.com/xiaohesong/ums/wiki/diff%E7%AE%97%E6%B3%95
那个要对应上:
现在可以了,好像不够 github 的给力,定得有点前
我只是加了个 id 在 每个 h1, h2, h3 标签之类的,然后 "#xxx" 就会定过去吧,现在马马虎虎能定过去
6666
我也就凑凑热闹,添砖加瓦嘛,哈哈
今天面试被问到了这个和 setState。fk
牛。。。