diff算法(一)
vdom 核心价值是最大程度的减少dom渲染的范围,通过把dom用js模拟,进行计算、对比,找出最小的更新范围去更新,这个对比的过程就是diff算法。所以说diff算法是vdom中最核心、最关键的部分,能在日常使用 vue React中体现出来(比如 为什么v-for一定要用key?下面会讲到)
常规:树diff的时间复杂度 O(n^3)
两棵树的遍历(遍历tree1、遍历tree2、排序,1000个节点,要计算 1亿次,算法不可用)
优化:优化时间复杂度到O(n)
- 只比较同一层级,不跨级比较;
- tag不相同,则直接删掉重建,不再深度比较;
- tag和key,两者都相同,则认为是相同节点,不再深度比较
1、只比较同一层级
举个形象的例子。
<!-- 之前 -->
<div> <!-- 层级1 -->
<p> <!-- 层级2 -->
<b> aoy </b> <!-- 层级3 -->
<span>diff</Span>
</P>
</div>
<!-- 之后 -->
<div> <!-- 层级1 -->
<p> <!-- 层级2 -->
<b> aoy </b> <!-- 层级3 -->
</p>
<span>diff</Span>
</div>
我们可能期望将< span >直接移动到< p>的后边,这是最优的操作。但是实际的diff操作是移除< p >里的< span >在创建一个新的< span >插到< p >的后边。
因为新加的在层级2,旧的在层级3,属于不同层级的比较。
2、tag不同,则直接删掉重建,不再深度比较
如上图,D和G节点的子节点相同,但是tag不同,则重新删除重建。
3.通过key来标识区分相同节点。
开发者可以通过 key prop 来暗示哪些子元素在不同的渲染下能保持稳定。
左图没有key值,所以老的节点全部删除,新的节点再全部创建。右图添加key值,所以只需要将A移动到B,将C移动到D,就可以得到与新树一样的老树。
面试题:为何v-for一定需要key并且还不能是index
关于 index 作为 key 的解释。
例如,旧的 dom 结构是这样的
<ul>
<li key="0">张一</li>
<li key="1">张二</li>
<li key="2">张三</li>
</ul>
新的 dom 结构是这样的
<ul>
<li key="0">张二</li>
<li key="1">张一</li>
<li key="2">张三</li>
</ul>
这种情况,如果拿 tag 和 key 作为标准来对比,那就出错了,张二和张一就无法互换位置了。
正确的 key 应该不能拿 index ,而是用一个唯一的 id 或者标识,如:
<ul>
<li key="zhangyi">张一</li>
<li key="zhanger">张二</li>
<li key="zhangsan">张三</li>
</ul>
这种 key ,顺序变了也没关系。
参考
https://segmentfault.com/a/1190000008782928