1.虚拟DOM的原理流程:
1.用JavaScript模拟DOM树,并且渲染这个DOM树
2.在实现的过程中比较新老DOM树,得到比较差异的对象
3.然后把差异的对象应用到渲染的DOM树。
下面是流程图:
虚拟的DOM的核心思想就是:对复杂的文档DOM结构,提供一种方便的工具,进行最小化的DOM操作。这句话过于抽象,却基本概括了虚拟DOM的设计思想。也就是说根据虚拟dom树最初渲染成真实dom,当数据变化,或者说是页面需要重新渲染的时候,会重新生成一个新的完整的虚拟dom,拿新的虚拟dom来和旧的虚拟dom做对比(使用diff算法),得到需要更新的地方之后,更新内容这样的话,就能大量减少真实dom的操作,提高性能。
1.1.实现虚拟DOM
下面是一个真实的DOM节点
<div id="my-heard">
<p>hello world</p>
<ul>
<li class="num">num1</li>
<li class="num">num2</li>
<li class="num">num3</li>
</ul>
</div>
下面用内存型对象(js内存对象)模拟DOM节点实现虚拟DOM。
let data = {
arr: []
}
//1.内存中生成一颗虚拟dom树
var VDom = {
tag: "div",
attr: {
id: "my-heard"
},
children: [
{ tag: "p", text: helloWorld },
{ tag: "ul", attrs: { className: "list-ul" } }
]
}
//2.将内存中的虚拟dom树初始化渲染成真实dom树
//3.当我们修改data里面的数据的时候
data.arr.push("<li>我是第一个li</li>")
data.arr.push("<li>我是第二个li</li>")
//4.将之前的虚拟dom树结合新的数据生成一颗新的虚拟dom树
var newDom = {
tag:"div",
attr:{
id:"my-heard"
},
children:[
{tag:"p",text:helloWorld},
{tag:"ul",attrs:{clasName:"list-ul"},children:[
{tag:"li",text:"我是第一个li"},
{tag:"li",text:"我是第一个li"}
]}
]
}
2.虚拟DOM的Diff算法
1.虚拟DOM中,在DOM的状态发生变化时,虚拟DOM会进行Diff运算,来更新只需要被替换的DOM,而不是全部重绘。
2.在Diff算法中,只平层的比较前后两棵DOM树的节点,没有进行深度的遍历。
Diff有以下4种情况:
1.如果节点类型改变,直接将旧节点卸载,替换为新节点,旧节点包括下面的子节点都将被卸载,如果新节点和旧节点仅仅是类型不同,但下面的所有子节点都一样时,这样做也是效率不高的一个地方。
2.节点类型不变,属性或者属性值改变,不会卸载节点,执行节点更新的操作。
3.文本改变,直接修改文字内容。
4.移动,增加,删除子节点时:
2-1:如果想在中间插入节点F,简单粗暴的做法是:卸载C,装载F,卸载D,装载C,卸载E,装载D,装载E。如下图:
2-2:如果为元素增加key后,Vue就能根据key,直接找到具体的位置进行操作,效率比较高。如下图:
3.v-for中的key属性
在v-for中提供key,一方面可以提高性能,一方面也会避免出错。
下面是vue中v-for不绑定key:Vue的v-for不绑定key,默认行为和绑定key="index"是差不多的,官方没有默认这种行为的情况下,会导致所有列表DOM重新渲染。key="index"这种行为无法最大化性能,比如在第一个元素插入新元素,会导致所有列表DOM重新渲染。绑定key后,发现绑定的key值的元素变化了,就重新渲染该元素,而不是全部元素都重新渲染,会提高性能。性能主要是DOM渲染影响。key绑定id一类的唯一键好处就是在列表中插入某个元素,不会像index那样导致后面的元素全部重新渲染,而是根据绑定id的元素是否变化渲染。
<div id="app">
<div>
<button @click="add">添加</button>
</div>
<ul>
<li v-for="item in arr" ><input type="checkbox">{{item}}</li>
</ul>
</div>
<script src="../base/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
arr:["华山派","武当派","峨嵋派"]
},
methods:{
add(){
this.arr.unshift("少林派")
}
}
})
一开始选中的是"华山派":
但是添加了"少林派"之后,选中的就变成"少林派"了:
这是因为没有给元素添加key属性,从而在渲染的时候采用的是上面提到的2-1的做法。
下面是vue中v-for绑定key:这个就地复用原则是指 如果数据项的顺序被改变,vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素
<div id="app">
<div>
<button @click="add">添加</button>
</div>
<ul>
<li v-for="(item,index) in arr" :key="item.id"><input type="checkbox">{{item}}</li>
</ul>
</div>
<script src="../base/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
arr:[
{name:"华山派",id:1},
{name:"武当派",id:2},
{name:"峨嵋派",id:3},
]
},
methods:{
add(){
this.arr.unshift( {name:"少林派",id:3})
}
}
})
一开始选中的是"华山派":
添加了"少林派"之后,选中的还是变成"华山派"了: