:key的作用以及为什么不能使用index作为key


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 管理可复用的元素

当不用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,因为那样和就地复用没啥区别。

5. 参考

https://v2.cn.vuejs.org/v2/api/#key

https://v2.cn.vuejs.org/v2/guide/conditional.html#%E7%94%A8-key-%E7%AE%A1%E7%90%86%E5%8F%AF%E5%A4%8D%E7%94%A8%E7%9A%84%E5%85%83%E7%B4%A0

https://juejin.cn/post/6844904053344829453#heading-7


文章作者: iamfugui
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 iamfugui !
评论
  目录