【vue前端面试】key的作用与原理/虚拟DOM/diff算法原理/代码效果
本文中涉及了v-for中key的取值、虚拟DOM与真实DOM、diff算法原理等。
v-for中经常使用key作为标识,这其中涉及了虚拟DOM与真实DOM、虚拟DOM的对比算法等。
key的取值有时也不可以简单的定为index。
真实DOM与虚拟DOM
虚拟DOM为react与vue两个框架都带来了跨平台的能力。
虚拟DOM实际是一层对真实DOM的抽象。以JS对象(vnode节点)作为基础的树,用对象的属性描述节点,最终通过一系列操作使这棵树映射到真实环境上,也就是真实DOM。
创建虚拟DOM就是为了更好的将虚拟节点渲染到页面视图中。
虚拟DOM对象的节点与真实DOM的属性一一照应。
虚拟DOM节点:Vnodes。
虚拟DOM是在内存中的,真实DOM是用户可以操作的DOM。
而 虚拟DOM的对比算法(diff算法)就是依赖key完成的。
虚拟DOM中key的作用
key是虚拟DOM对象的标识,当状态中的数据发生变化的时候,vue会根据新数据生成新的虚拟DOM。然后vue会进行新虚拟DOM与旧虚拟DOM的比较【diff算法】。
diff算法原理
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 如果虚拟DOM中的内容没变,直接使用之前的真实DOM.
- 如果虚拟DOM中的内容改变,则生成新的真实DOM,并替换页面中之前的旧真实DOM。
- 旧虚拟DOM中未找到与新虚拟DOM相同的key:
- 创建新的真实DOM,然后渲染到页面中。
总结来说,diff算法就是通过比较虚拟dom中vnode的key与内容,得出一些需要修改的最小单位,再更新视图。减小了dom的操作,提高了性能。
使用index作为key
使用index作为key使用是可能出现问题的。
- 如果对数据进行逆序添加/删除等破坏顺序操作,会产生没有必要的DOM更新。(页面效果没有问题,但是DOM会有不需要的更新,效率较低)。
- 如果本身结构中还包括输入类的DOM(比如input输入框):会产生DOM的错误更新,导致页面效果出错。
一个例子:【对数据进行逆序添加】
初始:页面展示一个人员列表,每个人员信息后有一个输入框(使用index作为key进行数组遍历)。
<body>
<div id="app">
<h2>人员列表(遍历数组)</h2>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}--{{p.age}}
<input type="text"></input>
</li>
</ul>
</div>
</body>
</html>
<script>
var vm = new Vue({
el:'#app',
data(){
return{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'里斯',age:10},
{id:'003',name:'王五',age:20},
]
}
}
})
</script>
大概是这个样子。
然后我们在输入框中写一些内容。为了便于我们辨别不同人员的输入框,在输入框里写每个人员的信息,大概如下:
然后我们在数组的起始位置(也就是张三的前一位)添加一个新的人员信息(老刘)。
<body>
<div id="app">
<h2>人员列表(遍历数组)</h2>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}--{{p.age}}
<input type="text"></input>
</li>
</ul>
<button @click="add">点我添加老刘</button>
</div>
</body>
</html>
<script>
var vm = new Vue({
el:'#app',
data(){
return{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'里斯',age:10},
{id:'003',name:'王五',age:20},
]
}
},
methods: {
add(){
this.persons.unshift({id:'004',name:'老刘',age:99})
}
}
})
</script>
点击按钮前:
点击按钮后:
这里发现在逆序添加数据的时候,原来的信息错位了。其实就是绑定key=index导致的。
然后我们看一下在添加人员信息时,虚拟dom与真实dom发生了怎样的变化:
1>初始数据
2>初始的虚拟dom(Vnodes)
3>初始的真实DOM(由虚拟DOM转化得到的真实DOM):
4>逆序添加数据
5>由新数据生成的新虚拟DOM:
这时,重点来了!!! 这里并不是根据新虚拟DOM构建新真实DOM,而是通过diff算法(让旧虚拟DOM 与 新虚拟DOM 进行比较,找出不同的地方再进行更新)。
可以看到新虚拟DOM的第一项是key为0,然后diff算法会到旧虚拟DOM中寻找是否存在key=0的节点,刚好找到了旧虚拟DOM中的第一行(张三18的数据),然后依次对比这一行的数据,发现只有里面的文字改变了(由张三18改为老刘30),input标签并没有改变。
然后新真实DOM会将没有改变的input从旧真实DOM中直接复用,而改变了的文字数据(老刘30)重新生成。
以此类推,造成了数据的错位。也就是下图这样:
使用id作为key
数据不会错乱,而且效率会更高。
上述例子中,如果将循环绑定的key值改为p.id
:
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}--{{p.age}}
<input type="text"></input>
</li>
就不会引起数据错乱。这时diff算法的工作流程:
新虚拟DOM的第一项是key为004,然后diff算法会到旧虚拟DOM中寻找是否存在key=004的节点,没有找到。将这一项直接生成新的真实DOM。
然后到了新虚拟DOM中的第二行(key=001),寻找旧虚拟DOM中是否有key=001的节点,刚好找到了旧虚拟DOM中的第一行(张三18的数据),然后依次对比这一行的数据,发现里面的文字内容和input标签都没有改变,直接将旧真实DOM中的这条复用过来。
以此类推,最终得到新的真实DOM,并且数据没有错位,效率也更高。
v-for遍历时不写key
vue默认将index索引值作为key进行遍历。
总结
1、开发中最好使用数据的唯一标识作为key,比如id、手机号、身份证号、学号等。
2、如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,只用于渲染列表进行展示,使用index作为key也是没有问题的。
更多推荐
所有评论(0)