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 算法的主要内容,如果未能解决你的问题,请参考以下文章