在 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. 在条件渲染中也可以使用 keyv-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 的习惯,并坚持使用唯一稳定标识,能避开一大类深坑。