虚拟DOM
虚拟DOM是什么
虚拟dom的本质一个能代表dom树的JS对象,包含tag、props、children三个属性
虚拟DOM的例子
React虚拟DOM
const vNode = {
key:null,
props: {
children: [
{type: 'span', ...},
{type: 'span', ...}
],
className: "red",
onClick:()=>{}
},
ref:null
type:'div',
...
}
//创建虚拟DOM
createElement('div',
{className:'red',
onClick:()=>{}},
[
createElement('span',{},'span1'),
createElement('span',{},'span2')
])
使用JSX写法
<div className="red" onClick="{()=>{}}">
<span>span1</span>
<span>span2</span>
</div>
可通过webpack babel插件转为createElement的形式
Vue虚拟dom
const vNode = {
tag:'div',
data:{
class:"red",
on:{
click: ()=>{}
}
},
children: [
{type: 'span', ...},
{type: 'span', ...}
],
...
}
//创建虚拟DOM(只能在render函数里得到h)
h('div',{
class:'red',
on:{click:()=>{}
},
[
h('span',{},'span1'),
h('span',{},'span2')
])
// Vue Template
<div class="red" @click="fn">
<span>span1</span>
<span>span2</span>
</div>
通过vue-loader 转为h的形式
虚拟DOM优点
-
减少DOM操作
虚拟DOM可以将多次操作合并为一次操作,比如添加1000个节点,却是一个接一个操作
-
跨平台
虚拟DOM不仅可以变成DOM,还可以变成小程序、ios应用、安卓应用、因为虚拟DOM本质上只是一个JS对象
DOM缺点
需要额外的创建函数,如createELement或h,但是可以通过JSX来简化成XML写法,会依赖打包工具。
DOM diff
DOM diff 是一个函数,我们称之为patch,patches = patch(oldVNode,newVNode),patch就是要运行的DOM操作。
例子:
<div :class="x">
<span v-if="y">{{string1}}</span>
<span>{{string2}}</span>
</div>
把虚拟DOM想象成树形
- 当数据发生变化时,x从red变成了green
DOM diff发现
div标签类型不变,只需要更新div对应的DOM属性,、 子元素没变,不更新
- 当数据发生变化时,y从true变成false
DOM diff发现
div没变,不更新
子元素1标签没变,但是children变了(hello->world),更新DOM内容
子元素2不见了,删除对应DOM
存在一些小小的问题
DOM diff在同层级对比中有bug。造成页面渲染错误。同一层级的一组节点可以通过唯一的id进行区分, 所以可以给节点设定唯一的key。从而消除bug。key只能是number和string类型,一定不要用index作为key值。
DOM diff大概逻辑
Tree diff
将新旧两颗树逐层对比,找出哪些节点需要更新
如果节点是组件就看Component diff
如果节点是标签就看Element diff
Component diff
如果节点是组件, 就先看组件类型
类型不同直接替换(删除旧的)
类型相同则只更新属性
然后深入组件做Tree diff
Element diff
如果节点是原生标签,则看标签名
标签名不同直接替换,相同则只更新属性
然后进入标签后代做Tree diff(递归)