Skip to content

Vue 中的 key

key 的作用

key 是虚拟 DOM diff 算法中的节点唯一标识。Vue 在对比新旧虚拟 DOM 树时,通过 key 判断两个节点是否代表同一个元素——相同 key 的节点可以复用,不同 key 的节点则需要销毁重建。

当列表数据发生变化(新增、删除、排序),Vue 需要确定哪些 DOM 节点可以原地保留、哪些需要移动或重新创建。key 就是这个判断过程的锚点。

缺少 key 或 key 不唯一时的行为

如果列表没有指定 key,Vue 会采用"就地复用"策略:默认按索引顺序对比新旧节点,逐个更新节点内容而非移动 DOM 位置。

html
<!-- 有 key:精确追踪 -->
<li v-for="item in items" :key="item.id">{{ item.name }}</li>

<!-- 无 key:就地复用 -->
<li v-for="item in items">{{ item.name }}</li>

就地复用在以下场景会引发问题:

  • 列表项带有组件状态。假设每一项包含一个 <input> 输入框,用户在第一个输入框中输入了内容,然后列表头部插入了一条新数据。无 key 时 Vue 会复用第一个 <li> 的 DOM,只是把它的文本从第一项更新为原来的第一项,输入框的状态会"停留"在页面上,但实际属于了错误的数据项。
  • 列表项带有过渡动画。缺少 key 时节点的移动操作会被替换为就地更新,动画效果会丢失。

有唯一 key 时的精确更新

为每个列表项指定唯一 key 后,Vue 能精确识别每个节点:

  • 新增项:创建新的 DOM 节点并插入正确位置
  • 删除项:移除对应的 DOM 节点
  • 排序项:移动已有的 DOM 节点到新位置,保留组件实例和 DOM 状态
html
<template v-for="task in taskList" :key="task.id">
  <TaskItem :task="task" />
</template>

taskList 顺序变化时,Vue 通过 task.id 识别每个 TaskItem 组件,移动对应 DOM 而非重新创建,组件内部的本地状态得到保留。

key 的索引问题

使用数组索引 index 作为 key 和使用不指定 key 的效果几乎等价——列表增删时,同一位置上的元素 index 会发生变化,导致 Vue 无法准确追踪。

唯一的例外是:列表是静态的、不会发生顺序变化,且列表项没有内部状态或输入控件,此时 index 作为 key 不会引发可见问题。但在实际项目中很难保证这些前提一直成立,因此始终建议使用稳定且唯一的标识作为 key