Appearance
diff 与 key
一句话结论
Vue 的 diff 用来比较新旧 vnode,并尽量复用已有 DOM。key 是 vnode 的身份标识,能帮助 Vue 判断哪些节点可以复用、移动或销毁。
diff 基本原则
- 只做同层比较,不跨层级移动节点。
- 标签类型不同,直接卸载旧节点并创建新节点。
- 标签类型相同,复用节点并更新属性、事件和子节点。
- 列表 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 的子节点会做更精细的比较:
- 从头部同步相同节点。
- 从尾部同步相同节点。
- 处理新增或删除的简单情况。
- 对乱序部分建立 key 到索引的映射。
- 使用最长递增子序列减少 DOM 移动次数。
最长递增子序列表示已经处在正确相对顺序中的节点,这些节点可以不移动,只移动剩下的节点。
常见追问
key 是不是越随机越好?
不是。随机 key 每次渲染都变,Vue 会认为节点全是新的,导致无法复用,组件状态也会被重置。key 必须稳定且能唯一代表业务项。
不写 key 会怎样?
Vue 会尽量就地复用节点。简单静态列表可能没问题,但动态列表容易出现状态错位。面试中可以回答:动态列表必须写稳定 key。
diff 为什么不做跨层级比较?
跨层级比较成本高,实际业务中跨层移动节点并不常见。框架选择同层比较,是性能和复杂度之间的工程取舍。