1. vue中key的作用
1)虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新的虚拟DOM】与【旧的虚拟DOM】的差异比较,比较规则如下:
A. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中的内容没变,直接使用之前的真实DOM;
- 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM;
B. 旧虚拟DOM中未找到与新虚拟DOM相同的key:
- 创建新的真实DOM,随后渲染到页面;
2)用index作为key可能会引发的问题
- 若对数据进行:逆序添加,逆序删除等破坏顺序的操作,会产生没必要的真实DOM更新==>界面效果没问题,但效率低
- 如果结构中包含输入类的DOM:会产生错误DOM更新 ==>界面有问题
3)开发中如何选择key?
- 最好使用每条数据的唯一标识作为key,比如id,手机号,身份证号,学号等唯一值;
- 如果不存在对数据逆序添加,逆序删除等破会顺序的操作,仅用于渲染列表用于展示,使用index作为key是没问题的。
2. 遍历时用index作为key和用唯一标识(如:id)作为key的区别
例如:以列表的形式展示一组人员信息。
<div id='root'>
<ul>
<li v-for="(p,index) of persons">
{{p.name}}-{{p.age}}
</li>
</ul>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
<ul>
<li v-for="(p,index) of persons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
persons: [
{ id: 1, name: '张三', age: 19 },
{ id: 2, name: '李四', age: 20 },
{ id: 3, name: '王五', age: 25 }
]
}
});
</script>

上面的示例中,不写key,以index为key,以id为key遍历数据时,得到的结果相同。由此可以看出:
- 遍历只做数据展示用时,不写key是没有任何影响的;
- key不会出现在真实DOM中;
- 实际上,即使不写key,Vue在生成真实DOM时,也用到了key,默认是数据索引(index)
如果将上述示例做个修改:
<div id='root'>
<button @click="add()">添加一个老刘</button>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
<ul>
<li v-for="(p,index) of persons" :key="p.id">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
persons: [
{ id: 1, name: '张三', age: 19 },
{ id: 2, name: '李四', age: 20 },
{ id: 3, name: '王五', age: 25 }
]
},
methods: {
add() {
const p = { id: 4, name: '老刘', age: 40};
this.persons.unshift(p);
}
}
});
</script>

上面的示例,如果在每个列表后加个输入框,并在输入框输入值,点击在列表前面添加一条数据,会发现以index为key和以id为key遍历显示的结果并不相同:

以index为key的渲染结果出现了错乱。

这是因为,向列表前面插入一条数据时,会先根据新数据生成新虚拟DOM,然后Vue通过虚拟DOM对比算法比较新的虚拟DOM与旧的虚拟DOM:首先找到key都为0的虚拟节点,该节点有两个子节点,一个是文本节点,另一个是输入框节点;首先对比到的是文本节点,两个文本内容不一样,用新的文本替换掉旧的文本节点,第二个对比的是输入框节点,两个节点一样,则直接复用原来的输入框;以此类推,后面的也一样。

id作为key时:首先比较key都为004的数据,比较时发现旧的虚拟DOM中并没有key为004 的数据,则直接生成新的真实DOM;接下来比较key为001的数据,key为001的文本节点和输入框节点一样,则直接复用旧的节点,后面的也一样。
两者的区别就是以id为key时只生成了一个新的节点,其他都是直接复用的旧的节点,相对来说,效率更高,而且不会出现渲染错误的问题。