key
这个在列表渲染中,极为重要,通常把数据中的id当做这个key值,这个id通常是由后端传过来的唯一标识符,例如uuid。
直接上代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>江河三千里</title>
<script type="text/javascript" src="http://ljy0427.online/js/vue.js"></script>
</head>
<body>
<div id="app">
<div v-for="(item,index) in users" :key="index">
<div>
{{item.name}}--{{item.age}}---{{index}}
<input type="text">
</div>
</div>
<button @click="addUser">添加用户</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
var vm = new Vue({
el: '#app',
data: {
users:
[
{ id:001,name: "江河", age: "18" },
{ id:002,name: "云月", age: "20" },
{ id:003,name: "三千", age: "21" },
{ id:004,name: "九千", age: "24" }
]
},
methods: {
addUser(){
var id=Math.floor(Math.random()*5)
const tr={ id:00+id,name: "TR"+id, age: "18" -id }
this.users.unshift(tr)
}
}
});
</script>
</html>
拟定这样的需求,就每次都给这个users添加用户,并且添加在第一个,首先将key值设置为index值
通过以上操作可以看错很大的问题,怎么我使用头插法添加元素,我之前所在input框中输入的东西,不对应了,乱了,而且乱得还很有规律,都随着元素的增加,input框中的值,都往前移了。
这个就是由于使用了index当做了key,做造成的。
原理
首先分清什么是虚拟dom和真实dom
虚拟dom是Vue所管理的东西,也是看不见的;而真实dom就是用户所看见的,所使用的元素。也就是当页面中的元素经过用户的操作所改变,这里实际操作的是真实dom,然后Vue捕获对应的事件,修改其中的虚拟dom,并且重新渲染到页面上。
案例分析
在这个案例中,当用户想input框中写入数据时,就是操作了真实dom,并没有修改其对应的虚拟dom,也就是内存地址。
当对应的数据有发生改变时,会根据新的数据生成虚拟dom,并且在这个过程中会产生很关键的一步,虚拟dom的对比,也就是改变前后两个虚拟dom的对比算法,而这个对比算法的依据就是key值,并且在对比过程中,如果前后的值不相同,后者的值将会覆盖前者的。
即将key值相等的元素进行比较。
解释
例如这里的第一个数据,以index作作为key,也就是江河这个用户的key为0,并且还对同一层级的input框进行了数据的修改。
后面通过点击事件,在users的一开始添加了用户TR,这种时侯,这个TR的key值就为0了,江河的key值就为1了。
说到这里,根据前面说的对比值,如果不相同,那么后者将覆盖前者啊。然后这个案例中,添加了TR用户之后,input框中的值啥也没有,这种时候,前后input对比的时候,都不相同了,不应该是后者,然后依次类推,应该是都没啊,就像如下。
的确,这样确实没毛病,但却忽视了一个很重要的问题,虚拟dom!
用户在对input框进行修改的时候,修改的是真实dom,并不是虚拟dom,也就是并没有修改到当前虚拟dom的内存地址,所以,在对比的时候,发现地址不变,就跳过了,里面的东西变没变,就不管了。因此就出现了添加了TR用户之后,其input框中的值是之前江河用户那一栏的数据了。
更换key
虽然在上述过程中,设置了key为index,但其实这样,就相当于没有设置。如果要设置,就需要将key值设为id这唯一标识符。
<body>
<div id="app">
<div v-for="(item,index) in users" :key="item.id">
<div>
{{item.name}}--{{item.age}}---{{item.id}}
<input type="text">
</div>
</div>
<button @click="addUser">添加用户</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
var vm = new Vue({
el: '#app',
data: {
users:
[
{ id:001,name: "江河", age: "18" },
{ id:002,name: "云月", age: "20" },
{ id:003,name: "三千", age: "21" },
{ id:004,name: "九千", age: "24" }
],
idArr:[1,2,3,4]
},
methods: {
addUser(){
var id=Math.floor(Math.random()*100)
var findIndex=this.idArr.indexOf(id);
if(findIndex==-1){
this.idArr.push(id);
const tr={ id:00+id,name: "TR"+id, age: "18" -id }
this.users.unshift(tr)
}else{
alert("当前用户id已存在,重新添加")
}
}
}
});
</script>
这种时候,通过用户的唯一标识来作为key,就解决了这个问题。
总结
在写这种v-for列表渲染的时候,尽量将key设置为后端传过来的唯一标识符。