前言
大家好,我是小杨,一个写了6年前端的老司机。今天要聊一个Vue里看似简单但90%新手都会踩的坑------用index当key。这玩意表面人畜无害,实际暗藏杀机,轻则数据错乱,重则性能爆炸。不信?往下看我的翻车实录!
一、index当key的灾难现场
上周我写了个代办事项列表,代码长这样:
html
<!-- 天真无邪的写法 -->
<ul>
<li v-for="(todo, index) in todos" :key="index">
{{ 我 }}的任务:{{ todo.text }}
</li>
</ul>
看起来没问题对吧?直到我加了删除功能...
翻车过程:
-
假设有三个任务:["买菜", "写代码", "摸鱼"]
-
删除第二个"写代码"后
-
Vue根据index认为:
- 旧节点0(买菜)→ 新节点0(买菜) ✔
- 旧节点1(写代码)→ 新节点1(摸鱼) ❌
- 旧节点2(摸鱼)→ 被删除
结果:本该显示["买菜", "摸鱼"],却可能触发组件异常复用,导致:
- 输入框状态错乱
- 动画效果错位
- 控制台疯狂警告
二、为什么index是万恶之源?
1. 数组变异操作直接暴击
- 当发生删除/插入时,所有后续元素的index全变了
- 但Vue觉得:"咦,key=1的节点还在嘛(其实内容已经物是人非)"
2. 性能杀手
看这个例子:
js
// 初始数据
const todos = [
{ id: 1, text: "买菜" },
{ id: 2, text: "写代码" }
]
// 头部插入新任务
todos.unshift({ id: 3, text: "开会" })
用index当key时:
- 所有
- 的key都变了(0→1, 1→2)
- 导致全部DOM重新渲染(其实只需要在头部加一个DOM)
用id当key时:
- 只有新加的
- 会创建
- 其他DOM原地复用
三、正确姿势指南
✅ 方案1:用数据唯一标识
html
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
✅ 方案2:实在没id怎么办?
js
// 方法1:前端生成唯一ID
todos.map(todo => ({
...todo,
_id: Math.random().toString(36).slice(2)
}))
// 方法2:用内容哈希(仅限内容不会重复)
:key="hash(todo.text)"
❌ 这些情况千万别用index!
- 列表有增删操作
- 列表项包含表单元素
- 需要过渡动画
- 列表项是有状态组件
四、血的教训
去年我们项目有个诡异bug:
- 表格允许拖拽排序
- 每行有个计数器组件
- 拖拽后计数器的值会随机交换
排查三天发现 :有人写了:key="index"
。改成:key="row.id"
后,世界和平了。
五、总结
- 🚫 index当key ≈ 埋地雷
- ✅ 唯一id当key = 稳如老狗
- 💡 没有id就创造id(但别用index!)
下次写v-for时,记得摸摸头顶------哦不,记得检查key!你的头发会感谢你。
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!