当前位置:首页 > VUE

vue diff实现

2026-02-10 06:38:31VUE

Vue 的 Diff 算法实现

Vue 的虚拟 DOM Diff 算法(即 patch 过程)通过高效比较新旧虚拟节点(VNode)来最小化 DOM 操作。其核心逻辑基于 同层比较双端比较 策略,以下是关键实现要点:

核心流程

同层比较
Diff 只在同一层级的 VNode 间进行,跨层级移动节点会直接销毁并重建,避免递归遍历带来的性能损耗。

双端比较
通过四个指针(新旧节点的头尾索引)逐步向中间靠拢,优先处理以下四种简单情况:

  1. 旧头 vs 新头
  2. 旧尾 vs 新尾
  3. 旧头 vs 新尾
  4. 旧尾 vs 新头

若均不匹配,则通过 key 查找可复用的节点。

关键代码逻辑

1. 节点复用条件
判断两个 VNode 是否可复用:

function sameVnode(a, b) {
  return (
    a.key === b.key && // key 相同
    a.tag === b.tag && // 标签类型相同
    a.isComment === b.isComment && // 注释节点相同
    isDef(a.data) === isDef(b.data) && // 数据属性存在性一致
    sameInputType(a, b) // 输入类型相同(针对 input 元素)
  )
}

2. 双端比较实现
以下是简化后的核心逻辑:

while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
  if (isUndef(oldStartVnode)) {
    oldStartVnode = oldCh[++oldStartIdx]
  } 
  else if (isUndef(oldEndVnode)) {
    oldEndVnode = oldCh[--oldEndIdx]
  }
  // 情况1:旧头 == 新头
  else if (sameVnode(oldStartVnode, newStartVnode)) {
    patchVnode(oldStartVnode, newStartVnode)
    oldStartVnode = oldCh[++oldStartIdx]
    newStartVnode = newCh[++newStartIdx]
  }
  // 情况2:旧尾 == 新尾
  else if (sameVnode(oldEndVnode, newEndVnode)) {
    patchVnode(oldEndVnode, newEndVnode)
    oldEndVnode = oldCh[--oldEndIdx]
    newEndVnode = newCh[--newEndIdx]
  }
  // 情况3:旧头 == 新尾(节点右移)
  else if (sameVnode(oldStartVnode, newEndVnode)) {
    patchVnode(oldStartVnode, newEndVnode)
    insertBefore(parentElm, oldStartVnode.elm, nextSibling(oldEndVnode.elm))
    oldStartVnode = oldCh[++oldStartIdx]
    newEndVnode = newCh[--newEndIdx]
  }
  // 情况4:旧尾 == 新头(节点左移)
  else if (sameVnode(oldEndVnode, newStartVnode)) {
    patchVnode(oldEndVnode, newStartVnode)
    insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
    oldEndVnode = oldCh[--oldEndIdx]
    newStartVnode = newCh[++newStartIdx]
  }
  // 情况5:通过 key 查找可复用节点
  else {
    const idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
    if (isUndef(idxInOld)) {
      createElm(newStartVnode, parentElm, oldStartVnode.elm)
    } 
    else {
      const vnodeToMove = oldCh[idxInOld]
      patchVnode(vnodeToMove, newStartVnode)
      insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
      oldCh[idxInOld] = undefined
    }
    newStartVnode = newCh[++newStartIdx]
  }
}

3. 剩余节点处理
循环结束后,若新旧节点列表有剩余:

  • 新列表有剩余:批量新增节点
  • 旧列表有剩余:批量删除节点

优化策略

key 的作用
key 是节点复用的关键标识。若未设置 key,Vue 会默认按位置复用,可能导致状态错乱或性能下降。

静态节点标记
编译阶段标记静态节点(isStatic),Diff 时直接跳过比较,提升性能。

vue diff实现

复杂度分析

  • 最优情况:头尾匹配时达到 O(n) 复杂度。
  • 最差情况:无 key 或乱序时退化为 O(n²),但实际场景中通过 key 优化可避免。

通过这种策略,Vue 在大多数实际应用中能高效更新 DOM,减少不必要的渲染开销。

标签: vuediff
分享给朋友:

相关文章

代码实现vue

代码实现vue

创建Vue项目 使用Vue CLI快速初始化项目,确保已安装Node.js和npm/yarn: npm install -g @vue/cli vue create my-vue-project c…

vue插件实现

vue插件实现

Vue 插件实现方法 Vue 插件是一种向 Vue 应用程序添加全局功能的机制。插件可以包含全局指令、过滤器、混入、实例方法等。 插件的基本结构 一个 Vue 插件通常是一个对象或函数,需要暴露一个…

实现vue table

实现vue table

Vue 表格实现方法 基础表格实现 使用 Vue 的 v-for 指令可以快速渲染表格数据。在模板中定义表格结构,通过数据绑定动态生成行和列。 <template> <tabl…

vue实现selectable

vue实现selectable

Vue 实现可选择的 Select 组件 在 Vue 中实现一个可选择的 Select 组件,可以通过以下几种方式: 使用原生 HTML select 元素 <template>…

vue 实现循环

vue 实现循环

Vue 实现循环的方法 在 Vue 中,可以使用 v-for 指令来实现循环渲染列表数据。以下是几种常见的循环实现方式: 基本数组循环 <template> <ul>…

vue实现单击

vue实现单击

Vue 实现单击事件的方法 在 Vue 中实现单击事件可以通过 v-on 指令或简写的 @ 符号绑定事件处理器。以下是几种常见的实现方式: 基础用法 通过 v-on:click 或 @click 绑…