vue中v-for的key的作用原理
一、原理
1.index作为key
1.1.首先我们要知道遍历数组的时候,如果不加key也不会报错,那为什么我们要加key呢?那就涉及到性能问题了。
1.2.我们都知道vue渲染数据的时候,并不是直接渲染成真实的DOM的,而是先渲染成虚拟的DOM,然后再渲染成真实的DOM的。那么在虚拟的DOM中就用到了key,因为虚拟DOM转换为真实的DOM的过程需要一个对比的过程,该对比过程需要diff算法,而diff算法需要用到key作为虚拟DOM中对象的标识。当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,然后vue对新的虚拟DOM和旧的虚拟DOM的差异进行比较,比较规则如下:
1.2.1.旧虚拟DOM中找到了与新虚拟DOM相同的key
1.2.2.若虚拟DOM中内容没变, 直接使用之前的真实DOM
1.2.3.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
1.2.4.旧虚拟DOM中未找到与新虚拟DOM相同的key
1.2.5.创建新的真实DOM,随后渲染到到页面
1.3.用index作为key可能会引发的问题
若对数据进行逆序添加、逆序删除等破坏顺序操作,可以正常进行,但效率低。
从下面的第一张图对比新旧的两个虚拟的DOM,我们发现旧的虚拟DOM中第一条列表li的key是索引值0,文本内容是张三,而新的虚拟DOM的第一个列表key是0,而文本内容是老刘,虽然两者的key相同,但是文本内容不同,则vue会创建新的真实DOM,将新的内容替换旧的内容。
在这里我们会发现index索引值作为key的性能问题就显示出来了,你往数据的头部添加数据的时候,你会发现所有的数据渲染到页面时都要重新生成,而没有复用。
在新的虚拟DOM中,key为0的列表和旧的虚拟DOM中的key为0的列表对比,发现内容不同,就重新创建新的真实DOM,以此类推,新的虚拟DOM中key为1的列表是张三,而旧的虚拟DOM中本来是有张三的,但是之前被替换了,导致后面的数据都对应不上,所以所有DOM结点都要重新创建,这就出现了之前说的性能问题,这就是为什么要根据具体场景使用index作为key。
2.id作为key
**2.1.**如果使用id或者能够作为唯一标识的字段作为key这就不会出现用index作为key的问题。
通过下面的图可以看出,新的虚拟DOM的第一个列表key为id=“004”,然后去旧的虚拟DOM中查找key为id="004"的列表,发现没有这个key为id=“4”这个列表,则就创建新的真实DOM。而其他的列表发现key、内容、元素结点input相同,则新的虚拟DOM就会复用旧的虚拟DOM的列表生成真实的DOM,这样虚拟DOM的好处就体现出来了,大大的减少了性能问题。
二、代码解释
<title>key的原理</title>
<script type="text/javascript" src="../js/vue.js"></script>
<div id="root">
<h2>人员列表(遍历数组)</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
persons: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 }
]
},
methods: {
add() {
const p = { id: '004', name: '老刘', age: 40 }
this.persons.unshift(p)
}
},
})
</script>
上面的代码通过添加了一个列表数据,里面同时有一个输入框,你往输入框里输入内容的时候,利用index作为key就会有问题,你会发现数据错乱了。因为老刘的索引和张三的索引都为0,但是内容不同,那老刘和张三肯定不同嘛,但是里面的input是相同的,根据diff算法,相同那就复用呗,所以老刘就复用了张三的input,所以就会出现上图的问题。但是利用id或能够作为唯一标识的字段作为id就正常。
我是正在学习前端的菜鸟,如有错误,请及时指出~