想要搞清楚v-for中key的作用,首先要了解虚拟DOM和diff算法。
虚拟DOM,简称VNode,其实是一颗以 JavaScript 为基础的树,是对真实DOM的抽象。虚拟 DOM 可以经过一系列转换变为真实 DOM 并渲染到页面上。
可以用虚拟 DOM 来描述一个简单的 Vue 组件:
<template>
<span class="demo" v-show="isShow">This is a span.</span>
</template>
对应的 VNode:
{
tag: 'span',
data: {
directives: [
{
rawName: 'v-show',
expression: 'isShow',
name: 'show',
value: true
}
],
staticClass: 'demo'
},
text: undefined,
children: [
{
tag: undefined,
data: undefined,
text: 'This is a span.',
children: undefined
}
]
}
假设现在已经有一颗 VNode 树,现在又有一颗 VNode 新树,现在需要把最新的这颗树更新上去要怎么操作呢?
最简单粗暴的方式就是拿最新的树把之前的树替换掉,然后重新渲染页面,这样确实可以解决问题,但肯定不是最佳解决方案。我们肯定要思考能不能只更新变化的部分,那些没有变化的元素不更新,这样不是更高效吗?
要想实现这种高效的方法,我们需要找到两棵树中变化的部分进行更新,而 diff 算法就是来解决这个问题的。
官方文档的解释:
当Vue正在使用 v-for 来渲染元素列表时,它默认使用“就地更新”的策略,如果数据项的顺序被改变,Vue 不会移动 DOM元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态(例如:表单输入值)的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,需要为每项提供一个唯一 key 。
不使用 key 的代码:
<template>
<ul>
<li v-for="item in arr">{{item}}</li>
</ul>
</template>
<script>
export default {
data() {
arr: ["A", "B", "C"],
},
created() {
setTimeOut(() => {
this.arr.splice(1, 0, "D");
}, 2000);
}
}
</script>
上面的代码就是把数组 [A, B, C] 变为 [A, D, B, C],同时页面也更新,diff过程如下图:
一共做了两次更新,一次插入操作。
接下来再看有 key 的情况:
<template>
<ul>
<li v-for="item in arr" :key="item">{{item}}</li>
</ul>
</template>
<script>
export default {
data() {
arr: ["A", "B", "C"],
},
created() {
setTimeOut(() => {
this.arr.splice(1, 0, "D");
}, 2000);
}
}
</script>
在有 key 的情况下,只进行了一次插入操作。
因此,v-for 中 for 的作用就是让每个被循环的元素都有一个唯一的身份标识,这样 Vue 就可以更加精准的追踪到每个元素,从而更加高效的更新页面,当然,如果没有 key 程序也不会报错,只不过此时,程序的效率会很低。