目录
引入
当你使用过Vue时,必定会对v-for的指令非常熟悉。该指令通常用于遍历数组数据。使用v-for指令时,必定离不开key属性。你是否了解该属性,该属性的作用是什么?取值有什么?分别又有什么区别呢?本文将详细介绍Vue中key的作用与原理!
扫描二维码关注公众号,回复: 16280132 查看本文章
key的作用
当你为一个列表绑定一个key属性时,该属性会存在于虚拟的DOM中,key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据用户对应修改的新数据,来生成新的虚拟DOM。随后Vue会进行新虚拟DOM与旧虚拟DOM的差异比较。进行比较的过程可以分成两种情况:
当旧虚拟DOM中找到了与新虚拟DOM相同的key时,若虚拟DOM中内容没变, 直接使用之前的真实DOM!若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
当旧虚拟DOM中未找到与新虚拟DOM相同的key时,创建新的真实DOM,随后渲染到到页面。
是不是有点懵,别着急,在下面的例子中会有详细地举例!!
Key的取值
key主要有以下的三种情况,现在我们举一个小案例来分别采用如下的方式,看看具体都有什么效果以及区别是什么。
我们在页面是渲染一个人员的列表,并添加一个按钮,当点击按钮时,会在人员列表前再添加一个新成员。(为啥要在列表前,这是一个关键的点!)
不绑定key属性
当我们在编写v-for时没有添加key属性时,并没有影响到我们想要的效果。对应的实现代码以及效果图如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>key的原理</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>人员列表</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<li v-for="p of persons">
{
{p.name}}-{
{p.age}}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
},
methods: {
add(){
const p = {id:'004',name:'老刘',age:40}
this.persons.unshift(p)
}
},
})
</script>
</html>
这时候可能会有同学会问,不写key属性不也能够正常地显示我们想要的效果吗?所以为何那么麻烦,多加一个key属性!其实,当你没有添加key属性时,系统会默认将绑定的key属性的值指向index。则会列表的索引,例如:{id:'001',name:'张三',age:18}的index值就为0,{id:'002',name:'李四',age:19}的index值就为1,依次类推。来为每一个li标签做标识。
Key值为index
当我们添加key属性,并将该属性的值指向为index时,具体它实现出来的效果就如上的没有添加key属性一样。但是具体的实现代码有一点小区别:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>key的原理</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>人员列表</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<!-- 绑定了key属性,并将其值指向于index -->
<li v-for="(p,index) of persons" :key="index">
{
{p.name}}-{
{p.age}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
},
methods: {
add(){
const p = {id:'004',name:'老刘',age:40}
this.persons.unshift(p)
}
},
})
</script>
</html>
对应的实现和上面的相同,也是能够正确地将效果显示出来。这种写法也是比较常见的。这并没有看出什么问题。但是倘若现在我在每一个列表的后面添加一个输入框,会出现什么样的效果呢?
<div id="root">
<h2>人员列表</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<!-- 绑定了key属性,并将其值指向于index -->
<li v-for="(p,index) of persons" :key="index">
{
{p.name}}-{
{p.age}}
<!-- 增加了input输入框 -->
<input type="text">
</li>
</ul>
</div>
通过以上的效果图我们可以很明显地看出了其出现的问题,但输入框中没有内容时,添加之后并没有什么问题。但是当我们在添加之前先将对应的内容输入到相应的输入框中后,我们会发现添加之后出现了问题。所以为何会出现这种问题呢?我们来看看下面的图形演示:
当我们把index作为key时,初始数据之后会根据数据生成虚拟的DOM,对应的li标签中的key值分别是0,1,2。当数据生成初始虚拟DOM之后,会创建新的真实DOM,随后渲染到到页面。
而当我们再添加一条数据时,会根据我们新的数据来生成新的虚拟DOM。新的虚拟DOM会与初始的也就是旧的虚拟DOM来进行对比,先找到对应相同的key值的li,若虚拟DOM中内容没变, 直接使用之前的真实DOM,若虚拟DOM中内容变了, 则生成新的真实DOM,对比发现key="0"中的内容有变化,从“张三-18”变成了“老刘-30”,随后替换掉页面中之前的真实DOM。而<input type="text">对比的结果是相同的,因此保存原本的内容。key="1"以及key="2"以此类推。而key="3"并没有在旧虚拟DOM中找到,因此创建新的真实DOM,随后渲染到到页面。
这下明白了吧!!
Key值为数据唯一标识
当我们把key属性的值赋为数据中的唯一标识时,又会出现怎么样的结果呢?我们将persons中的id值赋给key属性。具体代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>key的原理</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>人员列表</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<!-- 绑定了key属性,并将其值指向于p.id -->
<li v-for="(p,index) of persons" :key="p.id">
{
{p.name}}-{
{p.age}}
<!-- 增加了input输入框 -->
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
},
methods: {
add(){
const p = {id:'004',name:'老刘',age:40}
this.persons.unshift(p)
}
},
})
</script>
</html>
我们可以明显地看出,若我们的key属性绑定的值为数据中的唯一标识时,我们就能够得到我们想要的结果。对应的原理图如下:
由于每一个li标签中绑定的值为id,因此当添加一个新成员时,其对应的id="004",对应的key值为004,并没有在旧的虚拟DOM中找到,因此添加到真实的DOM中并渲染在页面上。其他的就不在多解释,原理都是相同的。
总结
若使用index作为key需要考虑下载两个问题:
其一,若对数据进行逆序添加、逆序删除等破坏顺序的操作时, 会产生没有必要的真实DOM更新 ,虽然界面效果没问题, 但效率低。其二,如果结构中还包含输入类的DOM,会产生错误DOM更新 ,界面会产生问题。
那么我们在开发的过程中该如何选择key呢?
首先最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,用index作为key是没有问题的。