key是vnode复用的唯一依据,vue仅通过key与tag是否一致判断是否复用:key相同且tag一致则复用vnode并patch更新;key相同但tag不同则丢弃重建;key缺失或重复则退化为就地复用,导致状态错乱。

Vue.js 虚拟 DOM 更新中的数据结构复用,核心在于 vnode 复用 和 真实 DOM 元素复用 两个层面,而它们都依赖于 key 的存在与合理性。没有 key 或 key 设计不当,vnode 就无法稳定映射,复用就会退化为“就地复用”,导致状态错乱。
key 是 vnode 复用的唯一依据
Vue 不靠标签名、内容或顺序判断是否复用,只认 key:
- 当新旧 vnode 的 key 相同且 tag/type 一致,Vue 直接复用该 vnode,并执行 patch 更新其 props、children 或 text
- 若 key 相同但 tag 不同(如
<div key="1"> → <code><span key="1"></span>),则丢弃旧 vnode,创建全新节点,不复用 - 若 key 缺失或重复,Vue 启用“就地复用”策略:按索引一一对应,旧 vnode[i] 强制 patch 到新 vnode[i],即使语义完全不同
- 输入框、富文本编辑器、视频播放器等有内部状态的元素,只要 vnode 被复用,DOM 元素就不销毁,状态自然保留
- 组件实例同样被复用:data、watch、computed、生命周期钩子均延续,不会触发 created/mounted 重建
- 移动操作(如拖拽排序)本质是 vnode 复用 + DOM 元素位置调整,而非删除再插入,性能更优
- 新头 === 旧头 → 头部稳定,直接 patch,双头后移
- 新尾 === 旧尾 → 尾部稳定,直接 patch,双尾前移
- 新头 === 旧尾 → 节点左移(如末尾项挪到开头),移动 DOM 并更新 vnode 引用
- 新尾 === 旧头 → 节点右移(如开头项挪到末尾),同理移动
- 以上都不成立时,才基于 key 在剩余旧节点中查找;查不到就新建 vnode
- 用
:key="index":列表增删时 index 变动,key 失稳,vnode 映射断裂,看似复用实则错位 - key 动态生成(如
:key="Math.random()"):每次渲染 key 都变,强制全量重建 - 同一组件多次渲染不同业务逻辑但 key 相同:vnode 复用,但组件内部未响应 props 变更,状态滞留
- 解决方式:key 必须唯一、稳定、可预测;组件内需监听关键 props 变化并同步内部状态
真实 DOM 元素复用由 vnode 复用决定
vnode 复用成功后,其关联的 elm(真实 DOM 元素引用)会被保留并重用于新结构:
双端比较加速复用判定过程
在列表更新中,Vue 不遍历全部旧节点找匹配,而是用四个指针快速试探首尾四种常见移动模式:
复用失效的典型场景与规避方式
复用不是万能的,设计不合理会绕过它:











