虚拟DOM
虚拟DOM是什么?
一个能代表DOM树的对象,通常含有标签名、标签上的属性、事件监听和子元素们,以及其他属性。
虚拟DOM长什么样子
- React
const vNode = {
key: null,
props: {
children: [ //子元素们
{type: 'span',...},
{type: 'span',...}
],
className: "red" //标签上的属性
onClick:()=>{} //事件
}
ref:null,
type:"div",// 标签名或组件名
...
}
- Vue
const vNode = {
tag: "div",// 标签名或组件名
data: {
class: "red",//标签上的属性
on: {
click: () => {}//事件
}
},
children: [ //子元素们
{type: 'span',...},
{type: 'span',...}
]
}
简化创建虚拟DOM
- React jsx
<div className="red" onClick="{()=>{}}">
<span>span1</span>
<span>span2</span>
</div>
- Vue Template
h('div',{
class: 'red',
on: {
click:()=>{}
}
},[h('span',{},'span1'),h('span',{},'span2')])
最终创建虚拟DOM的方法
- React
通过babel转为createElement形式
<div className="red" onClick={fn}>
<span>span1</span>
<span>span2</span>
</div>
- Vue Template
通过vue-loader转为h形式
<div class="red" @click="fn">
<span>span1</span>
<span>span2</span>
</div>
虚拟DOM的优点
减少DOM操作次数
虚拟DOM可以将多次操作合并为一次操作(减少操作次数),比如要添加1000个节点,如果不用虚拟DOM操作,是要一个接一个操作的。
虚拟DOM借助DOM diff可以把多余的操作省掉,比如要添加1000个节点,其实只有10个是新增的。(减少操作次数)
跨平台
虚拟DOM不仅可以变成DOM,还可以变成小程序、iOS应用、安卓应用,因为虚拟DOM本质上只是一个JS对象。
虚拟DOM的缺点
需要额外的创建函数,如createElement或h,但可以通过JSX来简化成XML写法。
DOM diff
DOM diff就是虚拟DOM的对比算法。
我们把虚拟DOM想像成树形,代码如下:
<div :class="x">
<span v-if="y">{string1}</span>
<span>{string2}</span>
</div>
代码对应的图示如下:
当数据变化时
- x从red变成green时
DOM diff发现:
- div标签类型没变,只需要更新div对应的DOM的属性
- 子元素没变,不更新
- y从true变成false时
DOM diff发现:
- div没变,不用更新
- 子元素1标签没变,但是children变了,更新DOM内容
- 子元素2不见了,删除对应的DOM
DOM diff的逻辑
Tree diff
1.将新旧两颗树逐层对比,找出哪些节点需要更新
2.如果节点是组件就看Component diff
3.如果节点是标签就看Element diff
Component diff
1.如果节点是组件,就先看组件类型
2.类型不同直接替换(删除旧的)
3.类型相同则只更新属性
4.深入组件做Tree diff(递归)
Element diff
1.如果节点是原生标签,则看标签名
2.标签名不同直接替换,相同则只更新属性
3.然后进入标签后代做Tree diff(递归)
DOM diff优点
可以做到只把变化的部分重新渲染,提高渲染效率
DOM diff缺点
DOM diff在同级对比中会有bug,造成页面渲染错误。这个bug的解决如下:
同一层级的一组节点可以通过唯一的id进行区分,可以给节点设定唯一的key值来消除bug。