Part3-1-4 Diff 算法

Posted 沿着路走到底

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Part3-1-4 Diff 算法相关的知识,希望对你有一定的参考价值。

 Diff 算法

渲染真实 DOM 的开销很大,DOM 操作会引起浏览器的重排和重绘,浏览器重新渲染页面是非常耗性能的。

Diff 的核心是当数据发生变化时,不直接操作 DOM,而是用 JS对象 来描述真实 DOM,当数据变化后,会先比较 JS对象 是否发生变化,找到所有变化后的位置,只去最小化的更新变化的位置,从而提高性能。

Snbbdom 根据 DOM 的特点对传统的diff算法做了优化

• DOM 操作时候很少会跨级别操作节点

• 只比较同级别的节点

执行过程


在对开始和结束节点比较的时候,总共有四种情况

• oldStartVnode / newStartVnode (旧开始节点 / 新开始节点)

• oldEndVnode / newEndVnode (旧结束节点 / 新结束节点)
• oldStartVnode / newEndVnode (旧开始节点 / 新结束节点)
• oldEndVnode / newStartVnode (旧结束节点 / 新开始节点)

旧开始节点 和 新开始节点之间的比较

首先将新开始节点与旧开始节点 调用 sameVnode 函数进行比较,如果是相同节点(key 和 sel 相同),调用 patchVnode 函数比较这2个节点内部的差异,然后更新到真实 DOM。

比较完成后,将索引移动到下一个节点的位置(oldStartIdx++ / newStartIdx++),再去比较第二个节点,把第二个节点作为开始节点进行比较。

旧结束节点 和 新结束节点 之间的比较

如果开始节点不是相同节点,会从后往前比较,比较旧的结束节点和新的结束节点是否是相同节点,如果是相同节点,调用 patchVnode 函数比较这2个节点内部的差异,然后更新到真实 DOM。

比较完成后,将索引移动到倒数第二个节点的位置(oldEndIdx-- / newEndIdx--),再去比较倒数第二个节点,把倒数第二个节点作为结束节点进行比较。

旧开始节点 和 新结束节点 之间的比较

如果旧的开始节点和新的结束节点是相同节点的话,调用 patchVnode 函数比较这2个节点内部的差异,然后更新到真实 DOM。当内部差异更新完成之后,将旧开始节点移动到最后。

然后移动索引,旧开始节点的索引移动到下一个位置,新结束索引的位置移动到前一个位置。

旧结束节点 和 新开始节点 之间的比较

如果旧的结束节点和新的开始节点是相同节点的话,调用 patchVnode 函数比较这2个节点内部的差异,然后更新到真实 DOM。当内部差异更新完成之后,将旧结束节点移动到最开始的位置。

如果以上四种情况都不满足时,说明开始和结束节点都不相同

首先遍历新的开始节点,在旧节点数组中,查找是否有相同节点,如果没有找到,说明此时的新开始节点是一个新的节点,此时需要创建一个新的 DOM 元素,插入到旧节点最前面的位置。

如果新开始节点在旧节点数组中找到了相同 key 值的节点,先判断 sel 是否相同,

如果 sel 不同,说明不是相同的节点,需要创建新的 DOM 元素,并插入到旧节点最前面的位置。如果 sel 相同,说明是相同节点,找到的这个旧节点与新开始节点通过patchVnode 函数进行比较,并更新到真实 DOM,然后将这个旧节点移动到最开始的位置。

当循环结束之后

当老节点的数组先遍历完(oldStartIdx > oldEndIdx),说明新节点有剩余,将这些剩余的节点创建对应的 DOM 元素,并插入到 老节点的末尾。

如果新节点的数组先遍历完(newStartIdx > newEndIdx),说明老节点有剩余,将老节点剩余的节点批量删除。

总结

如果是相同节点的话,会重用之前旧节点对应的 DOM 元素,在 patchVnode 函数中会对比新旧节点之间的差异,然后把差异更新到重用的 DOM 元素上。这个差异可能是文本内容不同,也可能是子元素不同,而当前 DOM 是不需要重新创建的,如果文本内容或子元素也都相同的话,是不会进行 DOM 操作的。虚拟 DOM 是通过这种方式提高性能的。

以上是关于Part3-1-4 Diff 算法的主要内容,如果未能解决你的问题,请参考以下文章

web前端diff 算法深入一下?

React的diff算法详解

Diff算法

深入理解React:diff 算法

十分详细的diff算法原理解析

React diff算法