blog
blog copied to clipboard
v-for 的时候不一定要写 key
用 Vue.js 和 React 的同学都知道,在写 v-for 的时候一定要加 key,因为用唯一标识作为 key 后能带来两个好处:
- 列表更新的性能会更好,见为什么使用 v-for 时必须添加唯一的 key?- 掘金
- 避免了循环体中有依赖子组件状态或临时 DOM 状态 (例如表单输入框) 时导致的渲染问题,见 Vue 2.0 中 v-for 里面的 “就地复用” 策略是什么? - 霸都丶傲天的回答 - 知乎
Vue CLI 甚至更进一步,将这条规则(require-v-for-key)加入了默认的 eslint 配置当中,如果在使用 v-for 的时候没有加 key,控制台就会抛错。
但是,强制写 key 也带来了一些问题。
key 如果不是唯一的,会导致界面不可用
用了 key 之后有一点不好的地方:key 必须是唯一的,否则就会报错,严重一点会导致界面点击无响应,只能刷新网页。
在现实的项目开发中,唯一标识一般是由后端提供的 id,这带来的隐患就是:前端界面会不会崩溃,完全取决于后端会不会给重复的 key。
还别说,我真碰到过这样的问题。
有一次后端上线之后,原本好好的城市选择功能突然不能用了,前端看了一下之后,发现城市的 id 重复了,原因是业务给城市分了层级,同一个城市可能会在不同的层级,所以原本唯一的 id 现在可能会重复,需要用 id 加上另一个新增的属性 level 才能确定数据的唯一性。
我相信前端一般都有一个共识:前端应该做一些兜底,避免后端接口的不正确导致界面不可用;前端应该识别出这种情况,让界面能继续使用,并且最好给用户一些提示。
对于数据缺失的情况,我们可以用 lodash.get 这类方法安全的读取数据,但对于 id 不能重复这一点,由前端来检查一是比较繁琐,二是成本较高,稍微大一点的数据就能阻塞掉线程,让用户觉得界面卡顿;不检查的话,就会出现万一 id 重复了,前端界面整个不可用的情况。
在这次的线上问题中,前端渲染的是静态内容,循环体中没有表单元素,不写 key 也是完全没问题的;城市的数据量也不多,写上 key 带来的性能提升是完全没感知的。这让我开始思考:前端要不要冒着界面不可用的风险,换取肉眼无感知的性能提升?
用 index 作为 key 跟不写 key 是一样的
Vue.js 是推荐大家写 key 的,甚至在 Vue CLI 中默认加入了这条规则,但在现实世界里,有很大一部分场景下,数组是没有唯一标识的。
聪明的同学想到了用 index 作为 key,但是这样做真的会比不写 key 更好吗?
性能方面,该不该用 index 作为 react 列表中的 key - GitHub 中对比了用 index 和不写 key 的各种情况,结合 Vue 2.0 v-for 中 :key 到底有什么用? - 方应杭的回答 - 知乎等相关文章中对于 Vue.js 不写 key 时的处理逻辑,我得出的结论是:不写 key 和用 index 作为 key,都是用的“就地复用”策略,所以用 index 作为 key 没有比不写 key 的性能更好。
另一方面,在前面提到的知乎回答中,作者用代码演示了不写 key 和加上唯一 key 后,表单元素的渲染情况,我在他的基础上又加了“用 index 作为 key”这一场景,见 https://codepen.io/lmk123/pen/XWreqOZ
事实说明,即使用 index 作为 key,也不能避免不写 key 带来的表单输入框的问题。
总结
我的结论如下:
- 使用后端提供的 id 作为 key 可能会出现由后端接口不正常引起的前端界面不可用的问题,这是不符合我的预期的
- 用 index 作为 key 没有比不写 key 更好,可能唯一的优点就是 Vue CLI 不会抛错了。
所以,我的观点是:
- 如果不是必须要 key 的情况(例如数据量很大,需要 key 提高性能,或者循环体中有表单元素,需要 key 避免渲染问题),不要写 key
- 用 index 作为 key 不如不写 key,可以加上
<!-- eslint-disable-next-line vue/require-v-for-key -->避免 eslint 抛错
文中如果有不对的地方或者有不同的看法,欢迎留言探讨。
看到有人用整个item作为key v-for="item in items" :key="item" 您怎么评价这种行为