v-for 中 key 的作用
理解 Vue 列表渲染中 key 属性对 Diff 算法和节点复用的影响
问题
在 Vue 的 v-for 中,key 属性有什么作用?为什么推荐使用唯一标识而不是索引?
解答
key 的作用
key 是 Vue 识别 VNode 的唯一标识,用于在 Diff 算法中判断新旧节点是否可以复用。
没有 key 的情况
Vue 采用”就地更新”策略,按索引顺序复用节点:
// 旧列表
['A', 'B', 'C']
// 新列表(在头部插入 D)
['D', 'A', 'B', 'C']
// 没有 key 时的更新过程:
// 索引 0: A -> D (更新内容)
// 索引 1: B -> A (更新内容)
// 索引 2: C -> B (更新内容)
// 索引 3: 创建 C
// 结果:更新了 3 次,创建了 1 次
有 key 的情况
Vue 通过 key 精确匹配可复用节点:
// 旧列表
[{ id: 1, text: 'A' }, { id: 2, text: 'B' }, { id: 3, text: 'C' }]
// 新列表(在头部插入 D)
[{ id: 4, text: 'D' }, { id: 1, text: 'A' }, { id: 2, text: 'B' }, { id: 3, text: 'C' }]
// 有 key 时的更新过程:
// key=1 的节点复用,移动位置
// key=2 的节点复用,移动位置
// key=3 的节点复用,移动位置
// key=4 的节点是新的,创建
// 结果:移动了 3 次,创建了 1 次(DOM 移动比更新内容开销小)
代码示例
<template>
<div>
<!-- 错误:使用 index 作为 key -->
<div v-for="(item, index) in list" :key="index">
<input v-model="item.value" />
{{ item.text }}
</div>
<!-- 正确:使用唯一标识作为 key -->
<div v-for="item in list" :key="item.id">
<input v-model="item.value" />
{{ item.text }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{ id: 1, text: 'A', value: '' },
{ id: 2, text: 'B', value: '' },
{ id: 3, text: 'C', value: '' }
]
}
},
methods: {
// 在头部插入时,用 index 作为 key 会导致输入框内容错位
addFirst() {
this.list.unshift({ id: Date.now(), text: 'New', value: '' })
}
}
}
</script>
为什么不用 index 作为 key
// 原列表
items: [
{ id: 1, text: 'A' }, // index: 0
{ id: 2, text: 'B' }, // index: 1
{ id: 3, text: 'C' } // index: 2
]
// 删除 B 后
items: [
{ id: 1, text: 'A' }, // index: 0 (key 不变)
{ id: 3, text: 'C' } // index: 1 (key 从 2 变成 1)
]
// 用 index 作为 key:Vue 认为 index=1 的节点内容从 B 变成了 C
// 用 id 作为 key:Vue 知道 id=2 的节点被删除,id=3 的节点保持不变
关键点
key是 VNode 的唯一标识,帮助 Diff 算法识别节点- 没有
key时采用就地更新策略,可能导致状态错乱 - 有
key时可以精确复用和移动节点,减少 DOM 操作 - 不要用
index作为key,列表变化时会导致错误复用 - 推荐使用数据的唯一标识(如 id)作为
key
目录