为什么需要虚拟DOM
提高dom渲染效率,高效更新
什么是虚拟DOM
本质上就是一个JS对象,用来描述你希望在屏幕上看到的内容,里面记录了dom节点的一些属性
比如template里标签结构
<template>
<div id="box">
<p class="my_p">123</p>
</div>
</template>
对应的虚拟DOM结构
const dom = {
type: 'div',
attributes: [{id: 'box'}],
children: {
type: 'p',
arrtibutes: [{class: 'my_p'}],
text: '123'
}
}
虚拟DOM如何实现高效更新
生成两颗虚拟dom树进行比较
流程如下:
- 初次渲染时,会根据model数据创建一个虚拟DOM对象(树)
data: {
list: [1, 2, 3, 4]
}
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
const dom = {
type: 'ul',
attributes: [],
children: [
{ type: 'li', attributes: [], text: '1'},
{ type: 'li', attributes: [], text: '2'},
{ type: 'li', attributes: [], text: '3'},
{ type: 'li', attributes: [], text: '4'},
]
}
- 根据虚拟DOM生成真正的DOM,渲染到页面
- 当数据变化后,会重新根据新的数据,创建新的虚拟DOM对象(树)
data: {
list: [1,2,3,4,5]
}
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
const dom = {
type: 'ul',
attributes: [],
children: [
{ type: 'li', attributes: [], text: '1'},
{ type: 'li', attributes: [], text: '2'},
{ type: 'li', attributes: [], text: '3'},
{ type: 'li', attributes: [], text: '4'},
{ type: 'li', attributes: [], text: '随机数'}
]
}
- 此时有两份虚拟dom,利用diff算法进行两份虚拟dom进行对比
diff算法
diff算法会从根节点开始,一层层的向下比较旧虚拟DOM和新虚拟DOM,如果在某一层的某个节点发现不同了,会先判断什么改变,再做出相应的处理
情况1:元素改变了
删除元素,重建元素
旧虚拟DOM(利用模版标签表示)
<div id="box">
<p class="my_p">123</p>
</div>
新虚拟DOM(利用模版标签表示)
<ul id="box">
<li class="my_p">123</li>
</ul>
情况2:元素没变,属性变
只更新属性
旧虚拟DOM(利用模版标签表示)
<ul class="active">
<li v-for="item in list">{{ item }}</li>
</ul>
新虚拟DOM(利用模版标签表示)
<ul class="">
<li v-for="item in list">{{ item }}</li>
</ul>
情况3:有 v-for 但是没有 key 属性
就地更新1,在li的后面添加元素
旧虚拟DOM(利用模版标签表示)
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
新虚拟DOM(利用模版标签表示)
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>随机数</li>
</ul>
就地更新2,在li的前面添加元素
旧虚拟DOM(利用模版标签表示)
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
新虚拟DOM(利用模版标签表示)
<ul>
<li>随机数</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
情况4:有 v-for 也有 key 属性(通过key来标识相同的节点)
1.key使用index
旧虚拟DOM(利用模版标签表示)
<ul>
<li key="0">1</li>
<li key="1">2</li>
<li key="2">3</li>
<li key="3">4</li>
</ul>
新虚拟DOM(利用模版标签表示)
<ul>
<li key="0">随机数</li>
<li key="1">1</li>
<li key="2">2</li>
<li key="3">3</li>
<li key="4">4</li>
</ul>
1.key不使用index,使用唯一不变值,这里key使用的是item,每一项的值
旧虚拟DOM(利用模版标签表示)
<ul>
<li key="1">1</li>
<li key="2">2</li>
<li key="3">3</li>
<li key="4">4</li>
</ul>
新虚拟DOM(利用模版标签表示)
<ul>
<li key="随机数">随机数</li>
<li key="1">1</li>
<li key="2">2</li>
<li key="3">3</li>
<li key="4">4</li>
</ul>
key值的重要性
如果每一个虚拟DOM节点没有一个key值,他就没有自己的一个名字。当我们在做新旧虚拟dom的比对时,旧状态的虚拟节点就难以与新状态的虚拟节点之间确立关系,就会直接的就地更新。当每一个虚拟节点都有唯一key值时,新旧状态的虚拟节点很快就能知道谁是谁,这样就极大的提升了diff算法的效率。