一.虚拟DOM
1.虚拟DOM是和真实DOM对应的概念
什么是虚拟DOM,就是一个能代表DOM树的对象,通常含有标签名,标签上的属性,事件监听和子元素们,已经其他属性
2.为什么说虚拟DOM比DOM更快:
- DOM操作慢是对比于JS原生API的,如数组操作
- 任何基于DOM的库(像vue和react)都不可能在操作DOM时比DOM快
3.虚拟DOM比DOM快的原因(或者说虚拟DOM的优点)
a.可以减少DOM操作
- 虚拟DOM可以将多次操作合并为一次操作,比如添加了1000个div节点,确实一个接一个操作的
- 虚拟DOM借助DOM diff可以把多余的操作省掉,比如添加1000个div节点,其实只有10个div是新增的
b.跨平台
- 虚拟DOM不仅可以变成DOM,还可以变成小程序,IOS应用,安卓应用,因为虚拟DOM本质上只是一个JS对象
4.代码示例:
- 虚拟DOM(react)代码:
这个对象表示了一个标签为div,子元素为两个span,className为red,点击事件调用一个函数的虚拟DOM
const vNode = {
key:null,
props:{
children:[
{type:'span',...},
{type:'span',...},
],
className:"red" //标签上的属性
onClick:()=>{} //事件
},
ref:null,
type:"div", //标签名or组件名
...
}
- 虚拟DOM(vue)代码:
这个对象表示了一个标签为div,子元素为两个span,data中class为red,点击事件on调用一个click函数的虚拟DOM
const vNode = {
tag:"div", //标签名or组件名
data:{
class:"red", //标签上的属性
on:{
click: ()=>{} //事件
}
},
children:[ //子元素们
{type:'span',...},
{type:'span',...},
],
...
}
5.如何创建虚拟DOM
(1)React中
React.createElement
createElement('div',{className:'red',onclick:()=> {}},[ createElement('span',{},'span1'), createElement('span',{},'span2') ]
)
简化代码(React),通过babel转为createElement形式:
<div className = "red" onClick={fn}>
<span>span1</span>
<span>span2</span>
</div>
(2)vue中
Vue只能在render函数里面得到h
h('div',{
class:'red',
on:{
click:()=>{}
},
},[h('span',{},'span1'),h('span',{},'span2'])
)
简化代码,Vue Template,通过vue-loader转为h形式
<div class="red" @click = "fn">
<span>span1</span>
<span>span2</span>
</div>
5.虚拟DOM的缺点:
需要额外的创建函数,如createElement或h,但可以通过JSX来简化成XML写法
而简化后的方法的缺点:严重依赖打包工具,否则js不认识
二.真实DOM和虚拟DOM谁更慢
1.规模较小的时候虚拟DOM是较快的,而规模较大的时候虚拟DOM是很慢的,原生DOM,或者说真实DOM是可以保证稳定性的
2.当规模只有数千以及以下时候,虚拟DOM会很好的优化一些操作
三.DOM diff(虚拟DOM的对比算法)
什么是DOM diff
就是一个函数,我们称之为patch patches = patch(oldVNode,newVNode) patches就是要运行的DOM操作,可能长这样:
[
{type:'INSERT',vNode:...},
{type:'TEXT',vNode:...},
{type:'PROPS',propsPatch:[...]},
]
DOM diff可能的大概逻辑
- Tree diff
将新旧两棵树逐层对比,找出哪些节点需要更新
如果节点是组件就看Component diff
如果节点是标签就看Element diff
- Component diff
如果节点是组件,就先看类型
类型不同直接替换(删除旧的)
类型相同则只更新属性
然后深入组件做Tree diff(递归)
- Element diff
如果节点是原生标签,则看签名
标签名不同直接替换,相同只更新属性
然后进入标签后代做Tree diff(递归)