1. key的作用
key是diff算法中的唯一标识符,加了key之后dom元素就和数据项互相绑定,可以让vue知道dom节点谁是谁,不会出现因为dom元素就地复用出现bug的情况。
案例讲解
代码如下:
<body>
<div id="app">
<button @click="remove">remove</button><button @click="add">add</button>
<div>用id作为key:</div>
<li v-for="(v,i) of list" :key="i">
<input :value="v.name">{{v.name}}
</li>
</div>
<script>
new Vue({
el: '#app',
data: {
list:[
{id:101,name:"A"},
{id:102,name:"B"},
{id:103,name:"C"},
{id:104,name:"D"}
],
id: 0,
},
methods: {
remove() {
this.list.splice(2, 1)
},
add() {
this.list.splice(2, 0,{id:this.id,name:"默认"})
this.id = ++this.id;
}
}
})
</script>
</body>
情况1,不使用key:
<li v-for="(v,i) of list" >
<input :value="v.name">{{v.name}}
</li>
手动修改控制台dom元素 (控制台元素中的value和界面中input值是分开输入的,下面的例子同是) 1-1
显示效果1-2
remove第三项数据后,1-3
效果1-4
发现不使用key的情况下dom元素不会发生新建,但是就地复用了应该删除的元素,并且对应该删除的元素及文本进行了重新渲染。对于其它绑定了动态数据的input组件进行重新渲染,对于其它动态文本没有进行重新渲染。
情况2,使用index作为key,并且文本绑定动态数据,input绑定静态数据时:
代码如下:
<li v-for="(v,i) of list" :key="i">
<input value="默认">{{v.name}}
</li>
在控制台手动修改dom元素
此时效果预览
点击remove按钮,期望移除第二个项数据,其他不变
点击remove按钮后的dom元素
此时效果预览
发现节点并没有新建,但是删除位置的文本会重新渲染。绑定的静态数据没有发生改变。我们可以得出结论,vue认为key为index(下标为2)的数据发生了改变,所以就重新渲染了此位置文本;而后面的元素节点没有数组做支持就被删除了。
情况3,使用index作为key时,文本绑定动态数据,input绑定动态数据时:
代码如下:
<li v-for="(v,i) of list" :key="i">
<input :value="v.name">{{v.name}}
</li>
在控制台手动修改dom元素 3-1
此时效果预览图 3-2
点击remove按钮,期望移除第二个项数据,其他不变
点击remove按钮后的dom元素 3-3
此时效果预览 3-4
发现节点并没有新建,但是删除位置的文本和所有绑定动态数据input进行了重新渲染。得出结论vue对于绑定动态数据节点的处理是不一样的,对于input这个可在界面上更改的节点会对其value进行重新渲染,而span文本这个不会重新渲染。
情况4,使用id作为key时,文本绑定动态数据,input绑定动态数据时:
<li v-for="(v,i) of list" :key="v.id" >
<input :value="v.name">{{v.name}}
</li>
直接删除第一项,看对后面元素的影响
remove() {
this.list.splice(0, 1)
},
在控制台手动修改dom元素 4-1
此时效果预览图 4-2
点击remove按钮后的dom元素 4-3
效果图
这时发现除了绑定了动态数据的imput value发生了重绘,没有其它改变。
2. 不用key行吗?
行。如以下官方例子,在切换登录方式时就不用key就很好:
当不用key时,vue会最大限度的节省dom元素开支。
又如分页每页5条,如果不加key,diff时反而可以复用结构,只改变值,不涉及到节点的创建删除,效率更好。
key的时候vue不知道你删了哪条元素吗?
实际上并不是,通过上面的例子可以得知vue会刷新被删除数组项之后的数据,而被删除之前的数据是不会被刷新的(也就是就地复用)。
3. 为什么key不推荐用index
因为index不能保证与dom元素进行唯一绑定,index会随着元素变化而变化,这实际上和就地复用一样(vue发现被删除的数据项key不变,数据改变,进行patch数据)。
在使用非文本节点的组件,且这个组件没有依赖于响应式的props,此时使用index作为key,那么此时对于列表的删除操作会导致视图错乱。
例子:在第二项数据上输入“静态数据二”,然后点击删除会发现“静态数据二”dom依然存在,而最后一项的dom消失了。
https://codesandbox.io/s/elastic-lewin-7f15gy?file=/src/App.vue
4. 总结
- key会与元素互相绑定。如果不使用key,vue采用就地复用的策略。例如ABC三个元素,你删了B的数据,那么实际上它会删掉C而保留B,但是你用key就不一样了,删了B它会保留A和C。
- 简单的渲染不必加key,利用就地复用反而能减少dom开支提高性能。
- 而长列表渲染就要加key了,因为“就地复用”可能会导致视图错误。
- 当你要去写key值的时候,最好不要用index,因为那样和就地复用没啥区别。