官方解析:Vue 中的 diff 算法是用于更新 Virtual DOM 树,从而实现高效的 DOM 操作。diff 算法会对比新旧两棵 Virtual DOM 树的差异,然后只更新必要的部分,从而减少 DOM 操作的次数。
Vue的diff算法是通过比较新旧虚拟DOM,以最小的代价来更新实际的DOM。由于频繁直接操作DOM的开销较大,Vue使用JavaScript对象模拟生成虚拟的VDOM,然后在虚拟的VDOM操作一次后再更新为真实的DOM。
整个流程首先将所有的节点转化为虚拟节点Vnode,在发生变更后与原本页面的旧节点OldNode进行比较。如果tag和key不同,则不进行比较直接替换;如果tag和key相同,则进行深入比较。深入比较节点的属性,再对节点的孩子逐个进行比较。
patch函数是diff算法的核心。首先,patch函数调用sameNode函数来对新旧节点进行比较,通过比较两个DOM的tag和key是否相同,如果相同则接着调用patchVnode函数进行深入比较,先比较它们节点的属性,再比较它们的孩子节点:
- 如果旧DOM无孩子而新DOM有,则直接进行添加操作。
- 如果旧DOM有孩子而新DOM没有,则直接进行删除操作。
- 如果旧DOM和新DOM都有,则调用updateChildren函数对它们的孩子节点进行比较。
updateChildren函数用于比较新旧节点的孩子列表。有多种实现方式,个人浅薄理解的一种是:四个指针分别指向新旧孩子列表的首尾,在首尾两两比较找到相同的节点。如果找到相同的节点,则移动旧节点的位置,对节点进行复用并移动对应的指针;如果没有相同的节点,则遍历旧孩子列表中与当前新孩子首节点相同节点,如果找到则可以复用,如果没有找到则创建插入。
举⼀个例子,看如下代码结构
<ul class="list">
<li class="item">item1</li>
<li class="item">item2</li>
<li class="item">item3</li>
</ul>
将第三个 item 修改为:
<ul class="list">
<li class="item">item1</li>
<li class="item">item2</li>
<li class="item">哈哈哈哈哈 item3</li>
</ul>
我们发现,实际上只有一个 li 标签的文本内容发生了修改,其他节点都保持不变。因此,并不需要对所有节点进行一次更新,只需更新这个 li 标签即可,Diff 算法就是用于找出需要更新的节点的算法。
总结:Diff 算法实际上是一种对比算法。对比的两者是新旧虚拟 DOM,通过对比找出哪个虚拟节点发生了变化。然后,只更新这个虚拟节点所对应的真实节点,而不需要更新其他没有发生变化的节点数据。这样实现了对真实 DOM 的精确更新,从而提高了效率。