Vue进阶 | Vue2 和Vue3 的 diff 算法

259 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

在前面的文章有分析过 Vue 2.x 的 diff 算法,可参考 Vue进阶 | 虚拟DOM(三)Diff算法,到了使用 Vue3.x 版本还是有所不同的。

在 Vue1.x 的时候,实际上还是在操作真实的 DOM,会创建专属的 Watcher给每个依赖,到了 Vue2.x,使用虚拟 DOM 提升了性能,通过虚拟DOM操作真实 DOM 来进行数据的更新。Vue3.x 同样也是虚拟 DOM。

在整个过程中,diff 算法的核心是调用 patch() 函数(将新旧的虚拟节点(VNode)对比)。下面先来介绍一下 diff 算法

diff算法

核心:调用 patch() 函数,对比新旧虚拟节点,计算出需要改动的最小变化,优化性能。

patch:patch(oldVnode, vnode, hydrating, removeOnly),其中oldVnode 和 Vnode分别表示新节点和旧节点

调用的时机:

  • 组件首次渲染(创建新的 vnode)
  • 数据发生变化时调用,给真实的 DOM 打补丁

特点:同层比较,深度优先

  • 先同级比较,然后比较子节点
    • 判断是否存在一边有子节点,一边没有子节点的情况
    • 再去比较都有子节点的情况(diff)
    • 如果两个节点相同,将旧节点的真实 DOM 赋值到新节点,有属性变化也更新进去
  • 递归比较子节点
    • 如果相同,执行上一步的步骤,如果不同,新节点递归创建新元素,销毁旧节点

下面画个草图体会一下:

image.png

时间复杂度:

在数据结构中,比较两棵树时的时间复杂度是 O(n^3)。

Vue在进行优化后,时间复杂度为 O(n) (在 Vue 中只有新旧 children 都为多个⼦节点时才需要⽤ diff 算法 同层⽐较)

Vue2 和Vue3 的 diff 算法的不同

在 Vue2 中,当数据发生变化,会新生成一个 DOM 树和旧的进行比较,每个节点都会比较,找到不同的节点后更新。

Vue3 对 diff 算法进行了进一步的优化,在创建虚拟 DOM 的时候,根据 DOM 中的内容是否会变化来为其添加一个静态标记。这样进行对比的时候,就只会对比带有静态标记的节点。

并且,Vue3 中使用了静态提升,不参与更新的元素只会创建一次,渲染的时候进行复用。