人人都会AI编程

key 属性的正确使用与常见错误

更新时间:2026-07-11

在 Vue 的列表渲染中,key 是一个看似不起眼却至关重要的属性。用对了,列表更新高效流畅;用错了,轻则性能下降,重则导致状态错乱、动画异常等难以排查的 bug。

key 是做什么的?

虚拟 DOM 进行 Diff 比较时,默认采用“就地复用”策略:当数据顺序变化时,Vue 会尽量复用已有的 DOM 元素,只更新变化的内容。这种策略虽然能减少 DOM 创建/销毁的开销,但在某些场景下会产生非预期的结果。

key 的作用是给每个虚拟节点一个唯一标识,让 Vue 在 Diff 过程中能够精准地追踪节点身份。当数据发生变化时,Vue 会根据 key 来判断哪些元素可以复用,哪些需要移除/新增,从而保证 DOM 的稳定性。

简单说:key 是虚拟 DOM 的身份证,让 Vue 知道“谁是谁”。

正确使用 key 的准则

1. 使用唯一且稳定的标识
最理想的情况是使用数据中的唯一 ID(如数据库主键)。没有 ID 时,也可用具备唯一性的字段组合,但要确保在列表的整个生命周期中保持稳定。

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

<!-- 如果没有 ID,且列表不会重新排序/增删,可用 index 临时过渡,但不推荐 -->
<li v-for="(item, index) in list" :key="index">{{ item.name }}</li>

2. key 必须绑定在 v-for 的直接子元素上
key 应该写在循环生成的元素(或组件)上,而不是它的子元素上。

<!-- 正确:key 在 <li> 上 -->
<li v-for="item in list" :key="item.id">
  <span>{{ item.name }}</span>
</li>

3. 在条件渲染中也可以使用 key
v-if/v-else/v-else-if 分支切换时,Vue 默认会尽可能复用相同类型的元素。如果希望在某些条件下强制替换元素(如重新触发过渡动画),可以给同一标签加上不同的 key,让 Vue 将它们视为两个独立元素。

<template>
  <div v-if="loginType === 'username'">
    <input placeholder="用户名" key="username" />
  </div>
  <div v-else>
    <input placeholder="邮箱" key="email" />
  </div>
</template>

常见错误

1. 使用索引 index 作为 key
这是最常见的性能陷阱。

<!-- 错误:用 index 作为 key -->
<li v-for="(item, index) in items" :key="index">{{ item.name }}</li>

当列表发生重排序、插入或删除时,index 会失效。比如在列表开头插入一项,所有后续项的 index 都会变化,导致 Vue 误认为原本的节点身份已改变,从而触发不必要的 DOM 移动/重绘,甚至导致组件状态错乱(如输入框内容错位)。只有当列表完全不会变动顺序、且不会动态增删时,index 作为 key 才是安全的——而这种场景极少。

2. key 值不唯一
如果 key 值在兄弟节点间存在重复,Vue 将无法正确识别节点身份,可能出现渲染错位或奇怪的更新行为。

<!-- 错误示例:key 重复 -->
<li v-for="item in list" :key="item.type">{{ item.name }}</li>
<!-- 若 list 中 type 存在重复,key 就不再唯一 -->

3. 使用随机数或时间戳作为 key
有人为了“永远不重复”,在渲染时生成随机 key。

<!-- 非常错误的做法 -->
<li v-for="item in list" :key="Math.random()">{{ item.name }}</li>

每次渲染都会生成全新的 key,导致 Vue 认为所有节点都是新的,从而销毁所有旧 DOM 并重新创建,完全丧失性能优化效果,还会导致不必要的组件生命周期重复、过渡动画失效等问题。

4. 忘记写 key
Vue 并不会因为你没写 key 就报错,但会回退到“就地复用”策略。在涉及交互状态(如输入框、动画)或频繁变动的列表时,缺少 key 会直接导致状态混乱。

一个真实的对比

假设有一个带输入框的列表,并支持在头部插入新项。

使用 index 作为 key 的结果:在顶部插入新项后,原本第一个输入框的内容会“跑到”第二个输入框,因为 Vue 将第一项的 DOM 复用给了新插入的项,而原第一项的内容留在了原地。

使用 item.id 作为 key 的结果:插入操作只会新增一个 DOM 节点,其他项输入框的内容完好无损。

什么时候可以不写 key?

  • 当列表内容非常简单且永远不会增删/重排(如静态配置项),省略 key 不会产生可见问题。但即便如此,加上 key 依然是更严谨的习惯。
  • 使用 <template v-for> 时,key 必须加在 <template> 标签上而非它的子元素。

总之,key 不是“有没有都行”的装饰,而是列表稳定更新的基石。养成随手写 key 的习惯,并坚持使用唯一稳定标识,能避开一大类深坑。