「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战」。
<span v-for="(time,index) in arr" :key="index">{{time}}<span>
嗨,大家好,我是Starqin,今天要讲述的就是上面代码中:key的作用,以及它的一些问题
假设我有以下数据
const vm = new Vue({
el: '.box',
data: {
userinof: [{
name: 'Starqin',
age: 22,
pro: '程序员',
hobby: '看科幻电影、电子产品'
}, {
name: 'W-js',
age: 23,
pro: '平面设计师',
hobby: '打篮球、汽车迷'
}]
}
})
接下来我将使用v-for将上面的数据渲染出来
渲染结果
思考一下,上面代码图中黄色框中的key有没有被浏览器解析到DOM中呢?
答案是没有的
key在哪?有什么用?
这是为什么呢?key去哪里呢?
其实key是存在的,只不过,这里的key是vue底层在使用,放在了vue虚拟DOM上,请看下图的对比
下面来说一下为什么vue的虚拟DOM上要有这个key
我们都知道,vue在将虚拟dom转为真实dom之前,都会做一遍Diff算法,将两个DOM做一个比较,渲染存在差异的部分,留下相同的部分,需要十分注意的是,这里的比较是对节点进行比较,而不是简单的对元素进行比较。
vue是以元素上的key值进行比较的,例如下图
当vue在进行比较的时候发现,原来的虚拟DOM上并没有key=2的存在,于是就会直接将姓名为张三的DOM渲染成真实DOM
也就是说,这里的key是支撑DIFF算法的关键。既然如此关键,那么这里的key我们可以不写吗?
答案是可以的,如果我们不写key,那么vue会默认将v-for的索引值作为key的值,不管我们写不写,其虚拟DOM上肯定有key这个属性存在。
key为索引时会出现什么BUG
既然Vue会默认生成key,那么为什么总有人要劝我多此一举,写上key呢?
带着疑问,我们来看一下,下面这个例子
现在我需要动态的将对象zangsan,插入到上面例子的前面(在Starqin的前面插入一个成员),并渲染到页面
<body>
<div class="box">
<button @click="addUser">点击我添加张三</button>
<div class="userbox" v-for="(v,i) in userinof" :key="i">
<p>姓名:{{v.name}}</p>
<p>爱好:{{v.hobby}}</p>
<p>年龄:{{v.age}}</p>
<p>职业:{{v.pro}}</p>
<input type="text">
</div>
</div>
<script>
const vm = new Vue({
el: '.box',
data: {
userinof: [{
name: 'Starqin',
age: 22,
pro: '程序员',
hobby: '看科幻电影、电子产品'
}, {
name: 'W-js',
age: 23,
pro: '平面设计师',
hobby: '打篮球、汽车迷'
}],
//这不是userinof数组中的元素,而是data下的对象
zansan: {
name: '张三',
age: 55,
pro: '家里蹲大学特聘吹牛专家教授',
hobby: '吹牛'
}
},
methods: {
addUser() {
this.userinof.unshift(this.zansan)
}
},
})
</script>
</body>
请看渲染页面图
注意,此时我新增了一个按钮,以及在模板中新增了一个
input框,当我点击这个按钮的时候,会将上面数据中的zansan对象追加到userinof数组的开头。还请大家注意在点击这个按钮之前两个输入框中的值,Starqin下的输入框中是Starqin,W-js下的input框中是W-js
当我点击按钮之后,程序运行的效果如下图
心细的您是不是发现,我输入框的文字发生了错位。这是为什么呢?难道这是vue渲染页面的BUG吗?其实不是,请让我们将视线重新聚焦到
key身上,请看虚拟DOM对比图
形成上面BUG的原因是,由于我们是在原来数组的前面插入了一个zangsan对象,这也就导致原来索引是0的Starqin,索引是1的W-js,现在变成了Starqin索引为1,W-sj索引为2,索引变化请看下图
再加上我们是以数组索引作为key的值的,最终导致新旧虚拟DOM依赖key值在做对比的时候,发现虚拟DOM对比图上红色框内的节点与旧虚拟DOM不一样,于是进行重新渲染,而绿色框中的节点一样,直接复用,最终形成了上面
input框发生错位的现象
解决Key为索引产生的BUG
怎么解决这个BUG呢?
其实解决这个问题很简单,我们只要将数据中的唯一的值赋值给key就可以完美解决这样的BUG,例如我将用户姓名作为唯一值赋值给key
修改后的部分代码
<div class="box">
<button @click="addUser">点击我添加张三</button>
<div class="userbox" v-for="(v,i) in userinof" :key="v.name">
<p>姓名:{{v.name}}</p>
<p>爱好:{{v.hobby}}</p>
<p>年龄:{{v.age}}</p>
<p>职业:{{v.pro}}</p>
<input type="text">
</div>
</div>
修改后点击按钮
问题完美解决\
这又是为什么呢?请看下图
此时新旧虚拟DOM绑定的key值都不会发生改变,形成了一一对应的关系,在作比较时,发现旧虚拟DOM上没有key值为张三的,于是直接在旧真实DOM上渲染了一个新元素。
总结:
- 如果数据具有唯一标识时,key值必须是那个唯一标识,没有时,随便。
- key的存在是为了更好的为Vue的diff算法提供支撑