简单介绍
在vue里面,我们通过v-for指令来渲染一个数组列表,列表项需要我们定义一个别名,即:
- { { item.message }}
在上面这个例子中,items
是我们要渲染的数组列表,item
是数组列表项的别名,当然,我们可以给v-for
添加第二个参数作为当前项的索引:
- { { index }} - { { item.message }}
有经验的同学可能会指出,上面的例子都少了一个属性: key
,是的,官方文档建议我们给v-for
渲染的每一个列表项指定一个key
,那么,key
的作用是什么呢?给了以下的解释:
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index" 。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有的唯一 id。这个特殊的属性相当于 Vue 1.x 的 track-by ,但它的工作方式类似于一个属性,所以你需要用 v-bind 来绑定动态值 (在这里使用简写):
复制代码
建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。因为它是 Vue 识别节点的一个通用机制,key 并不与 v-for 特别关联,key 还具有其他用途,我们将在后面的指南中看到其他用途。
提出问题
问题1: 什么是'就地复用'?
这里我举一个简单的例子来说明一下:
在一个玩具组装车间,小A, 小B, 小C,小D以及其他96个流水线工人一起完成组装玩具的工作,每个人都有自己的一辆玩具在组装,突然,小B肚子痛跑去上厕所了,但是小B的工作还没完成,为了不影响整体进度,工人都依次往前补,小C顶替了小B的工作,小D顶替了小C的工作,以此类推,这样虽然也是有点麻烦,还好工作按期完成了。
这就是就地复用大概的思路,如果不采用就地复用,那么,当小B缺席之后,流水线上的工人(包括小B之前的)都要重新排序,100个人按照工号重新排序,重新获取自己的工作内容,这样子工作效率就大大降低了。
在DOM中,就地复用的是那些没有变化的元素,例如:
{ {item.message}}复制代码
在这段代码中,没有变化的元素就是input
,当我们删除items的某个元素时,item.message
会发生变化,因此渲染的数据发生了变化,但是已经存在dom中的input
却会被就地复用,具体的演示效果如下:
问题2: 如何解决就地复用产生的问题?
正如我们上面看到的那样,我们希望input
也被重新渲染,这个问题的解决方案vue已经在文档中说的很清楚了,我们需要给每一个列表项加上一个唯一的key
属性
问题3: key属性是如何解决问题的,它的原理是什么?
这个问题就涉及到了虚拟DOM的diff算法了,具体原理可以查看
问题4: 没有添加key属性或者key属性不是唯一的会出现什么坑?
key属性需要绑定一个唯一的值,但是在我们的实际项目中,不一定会有这么一个唯一的值让我们绑定,因此习惯性我们会采用index
作为key
属性的值,这样做就容易产生问题了...
因为index对应的value值是会变化的,例如:
list: [ {index: 0, value: 0}, {index: 1, value: 1}, {index: 2, value: 2}]复制代码
当我们执行list.splice(1, 1)
移除index = 1的项时,list数组就会变成:
list: [ {index: 0, value: 0}, {index: 1, value: 2}]复制代码
因为index
是数组元素的索引,当某个元素被移除时,被移除元素后面的元素索引都会更新,也就是说index
并不是唯一的,所以vue就会采用 就地复用 原则,这时如果页面上有类似于input
这种跟value
值没有绑定关系的元素时,这些元素将会被复用,有可能就得不到我们要的效果了
总结: 相信用vue的人都会遇到这么一个列表渲染元素混乱的问题,key
属性看似简单但是第一次遇到的时候是会很纳闷的,关于列表渲染比较常见的坑还有数组和对象的监测和更新,this.$set
你值得拥有,详情可以查看,本文如果有错漏,欢迎指出~