blog icon indicating copy to clipboard operation
blog copied to clipboard

v-for 的时候不一定要写 key

Open lmk123 opened this issue 6 years ago • 2 comments

用 Vue.js 和 React 的同学都知道,在写 v-for 的时候一定要加 key,因为用唯一标识作为 key 后能带来两个好处:

  1. 列表更新的性能会更好,见为什么使用 v-for 时必须添加唯一的 key?- 掘金
  2. 避免了循环体中有依赖子组件状态或临时 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 抛错

文中如果有不对的地方或者有不同的看法,欢迎留言探讨。

lmk123 avatar Sep 03 '19 10:09 lmk123

看到有人用整个item作为key v-for="item in items" :key="item" 您怎么评价这种行为

AuroraTea avatar Jul 14 '23 14:07 AuroraTea