Skip to content

diff 与 key

一句话结论

Vue 的 diff 用来比较新旧 vnode,并尽量复用已有 DOM。key 是 vnode 的身份标识,能帮助 Vue 判断哪些节点可以复用、移动或销毁。

diff 基本原则

  1. 只做同层比较,不跨层级移动节点。
  2. 标签类型不同,直接卸载旧节点并创建新节点。
  3. 标签类型相同,复用节点并更新属性、事件和子节点。
  4. 列表 diff 依赖 key 判断节点身份。

key 为什么重要

vue
<li v-for="item in list" :key="item.id">
  {{ item.name }}
</li>

稳定的 key 可以让 Vue 正确识别同一个业务项。列表插入、删除、排序时,Vue 能复用真实没变的节点,只移动需要移动的节点。

为什么不建议用 index 做 key

当列表只追加且不排序时,index 问题不大。但如果中间插入、删除或排序,index 会变化,Vue 可能错误复用节点,导致输入框内容错位、组件状态错乱、动画异常。

Vue 3 列表 diff

Vue 3 对有 key 的子节点会做更精细的比较:

  1. 从头部同步相同节点。
  2. 从尾部同步相同节点。
  3. 处理新增或删除的简单情况。
  4. 对乱序部分建立 key 到索引的映射。
  5. 使用最长递增子序列减少 DOM 移动次数。

最长递增子序列表示已经处在正确相对顺序中的节点,这些节点可以不移动,只移动剩下的节点。

常见追问

key 是不是越随机越好?

不是。随机 key 每次渲染都变,Vue 会认为节点全是新的,导致无法复用,组件状态也会被重置。key 必须稳定且能唯一代表业务项。

不写 key 会怎样?

Vue 会尽量就地复用节点。简单静态列表可能没问题,但动态列表容易出现状态错位。面试中可以回答:动态列表必须写稳定 key。

diff 为什么不做跨层级比较?

跨层级比较成本高,实际业务中跨层移动节点并不常见。框架选择同层比较,是性能和复杂度之间的工程取舍。