vue.js的diff算法在v-for更新时采用首尾双指针策略,通过头头、尾尾、尾头、头尾四种匹配情形实现o(n)高效复用与移动节点,未命中时借助key哈希查找优化,最终处理剩余节点完成增删。

Vue.js 的 Diff 算法在 v-for 列表更新时,优先尝试用“首尾双指针”策略快速匹配节点,避免逐个比对,显著提升性能。其核心不是暴力遍历,而是利用真实 DOM 节点的可预测性,在多数常见更新场景(如追加、删除、移动)中做到 O(n) 时间复杂度。
首尾双指针的基本结构
新旧两组 vnode 分别维护四个索引:
- oldStartIdx / oldEndIdx:指向旧节点数组的起始与末尾
- newStartIdx / newEndIdx:指向新节点数组的起始与末尾
- 对应节点分别是
oldStartVnode、oldEndVnode、newStartVnode、newEndVnode
算法持续收缩区间,直到任一端指针交错(oldStartIdx > oldEndIdx 或 newStartIdx > newEndIdx)为止。
四种首尾快速匹配情形
每轮循环优先判断以下四种情况,满足即执行对应 DOM 操作并移动指针:
-
头头相同:
oldStartVnode.key === newStartVnode.key→ 复用节点,patch(oldStartVnode, newStartVnode),然后oldStartIdx++、newStartIdx++ -
尾尾相同:
oldEndVnode.key === newEndVnode.key→ 复用节点,patch(oldEndVnode, newEndVnode),然后oldEndIdx--、newEndIdx-- -
尾头相同:
oldEndVnode.key === newStartVnode.key→ 将oldEndVnode对应的真实 DOM 移动到oldStartVnode前面,再patch,然后oldEndIdx--、newStartIdx++ -
头尾相同:
oldStartVnode.key === newEndVnode.key→ 将oldStartVnode对应的真实 DOM 移动到oldEndVnode后面,再patch,然后oldStartIdx++、newEndIdx--
这四步覆盖了插入、删除、翻转、内部移动等高频操作,无需查找或新建节点。
未命中时的 fallback 处理
当四种情形都不满足,说明当前首尾无法直接复用,进入查找逻辑:
- 用
newStartVnode.key在旧节点剩余区间([oldStartIdx, oldEndIdx])中查找匹配项 - 若找到,复用该节点并移动至
oldStartVnode前;若未找到,则创建新节点插入 - 无论是否找到,都执行
newStartIdx++,继续处理下一个新节点
该查找过程使用哈希映射(keyToOldIdx)优化,平均 O(1),避免嵌套循环。
收尾阶段的增删逻辑
双指针结束后,可能仍有剩余节点:
-
旧节点有剩(
oldStartIdx ):这些节点已不在新列表中,批量卸载(removeVnodes) -
新节点有剩(
newStartIdx ):这些是新增项,批量挂载(addVnodes),插入位置根据上下文 vnode 自动推导(如插在oldEndVnode.el.nextSibling或oldStartVnode.el前)
整个流程不依赖全局 key 唯一性校验,仅基于 key 的存在性做高效复用决策,兼顾正确性与性能。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











