在 Vue 项目中,key 是一个大家经常会用到的属性,尤其在渲染列表的时候。用过 v-for 的同学都知道,key 是个“必填项”,但很多人写的时候,要么直接用 index(索引),要么偷懒用 Math.random()(随机数)。乍一看没啥问题,页面也能正常跑,结果越写越感觉不对劲:
- 数据顺序一变,显示错乱了。
- 切换分页,列表疯狂重渲染。
- 状态丢失,用户刚输入的东西瞬间没了。
这些坑可能很多人都踩过。今天咱们就来聊聊,为啥不建议用 index 或随机数作为 key,以及实际项目中应该怎么写才稳妥。
- KEY 的作用到底是啥?
在 Vue 里,key 是用来唯一标识列表每一项的,就像身份证号码那样,它的作用主要有两个:
- 定位更新:当数据发生变化时,Vue 会通过 key 来对比新旧节点,快速找到改动的地方。如果没有 key,Vue 就只能从头开始一个个比,效率会很低。
- 状态正确:key 能让 Vue 知道每个节点对应的是什么数据,确保在数据变化时,节点的状态不乱套。
一句话总结:key 就是告诉 Vue,“这个节点是谁,和哪个数据绑定”。
- 为什么不能用 INDEX?
场景一:列表增删导致状态错乱
假设我们有一个商品列表,支持删除功能:
<template>
<ul>
<li v-for="(item, index) in products" :key="index">
<span>{{ item.name }}</span>
<button @click="remove(index)">删除</button>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
products: [
{ name: '苹果' },
{ name: '香蕉' },
{ name: '橙子' },
],
};
},
methods: {
remove(index) {
this.products.splice(index, 1);
},
},
};
</script>
删除“苹果”之后,原来的列表:
index=0 苹果
index=1 香蕉
index=2 橙子
变成:
index=0 香蕉
index=1 橙子
Vue 的 key 是按 index 匹配的:
- 它以为 index=0 还是“苹果”,就把第一个 DOM 节点复用了。
- 原来的“香蕉”变成了“苹果”,显示错乱。
最终结果:删除一个商品,剩下的商品全乱套,简直是灾难。
场景二:拖拽排序出问题
假设你有一个任务列表,支持拖拽排序:
<template>
<ul>
<li v-for="(task, index) in tasks" :key="index">
<input v-model="task.name" />
</li>
</ul>
</template>
<script>
export default {
data() {
return {
tasks: [
{ name: '任务A' },
{ name: '任务B' },
{ name: '任务C' },
],
};
},
};
</script>
用户拖拽,把“任务C”拉到第一位:
拖拽前:任务A -> 任务B -> 任务C
拖拽后:任务C -> 任务A -> 任务B
因为 key 用的是 index,Vue 认为:
- index=0 还是原来的节点,复用后显示“任务A”。
- index=1 复用后显示“任务B”。
最终结果:数据变了,DOM 却没跟着变,输入框的内容一片混乱。
总结:index 这个东西,会随着数组的增删改、顺序变化而变,它并不是一个稳定的标识,所以不能用它作为 key。
- 为什么不能用随机数?
有些人发现 index 不行,就直接用随机数:
<li v-for="item in list" :key="Math.random()">{{ item.name }}</li>
但这种做法问题更大,原因很简单:随机数每次都会变。
场景:切换分页,列表疯狂重渲染
假设你有一个分页组件,每页显示 10 条数据,切换分页的时候:
- 因为每个 key 都是随机的,Vue 认为所有节点都不一样。
- 旧的 DOM 全部销毁,新的 DOM 全部重新创建。
哪怕两个分页的数据完全一样,也会引发大量的 DOM 操作,白白浪费性能,甚至可能导致页面卡顿。
更糟糕的是,如果列表中的某些项有用户输入状态,比如表单输入框,这些状态也会因为 DOM 被销毁而丢失。
总结:随机数是完全不稳定的,每次都会变,和用没用 key 没啥区别。
- 实际开发中应该怎么写 KEY?
方案一:用唯一的 ID(最佳选择)
如果数据有唯一的 id(比如数据库里的主键),直接用它就行:
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
这个 id 是稳定且唯一的,数据怎么变,key 都不会乱。
方案二:没有 ID 的情况下怎么办?
方法 1:生成唯一标识符
如果你的数据没有 id,可以在加载数据时给每项生成一个唯一的标识符:
list = list.map((item, index) => ({
...item,
id: `unique-${index}-${Date.now()}`,
}));
然后直接用这个 id 作为 key。
方法 2:用 JSON 字符串
如果数据内容是稳定的,可以用 JSON 序列化后的字符串作为 key:
<li v-for="item in list" :key="JSON.stringify(item)">{{ item.name }}</li>
注意:这种方法适合小型列表,数据量大的时候性能可能会受影响。
总结
- 为什么不能用 index
- 数组顺序变化会导致节点复用错误,页面显示乱套。
- 增删数据时,DOM 状态可能不对应,影响用户体验。
- 为什么不能用随机数
- 每次渲染都会生成新节点,导致性能下降。
- 用户输入等状态会丢失,用户体验差。
- 正确的做法
- 如果数据有唯一的 id,直接用它。
- 如果没有 id,生成一个唯一标识符或者用稳定的内容生成 key。
key 看似简单,却能决定页面的性能和状态是否正确。写好 key,不仅是提升代码质量的基本功,也能让项目少踩很多坑。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。