WEB面试题_虚拟DOM与DOM Diff 的原理
程序员文章站
2022-06-09 17:02:53
...
虚拟DOM与DOM Diff 的原理
虚拟DOM
DOM 操作慢是对比于JS原生API,如数组操作,任何基于DOM的库(VUE/React)都不可能在操作DOM时比DOM快。
他是什么?
一个能代表DOM树的对象,同窗含有标签名、标签上的属性、事件监听和子元素们,以及其他属性。
(React中)虚拟DOM的样子
const vNode = {
key:null,
props:{
children:[ //子元素们
{type:'span',...},
{type:'span',...}
],
className:"red",//标签上的属性
onClick:() => {} //事件
},
ref:null,
type:"div", //标签名 or 组件名
...
//有个div里面有两个子元素span,div有class和事件监听,onClick事件是一个函数,的虚拟dom。
}
(Vue中)虚拟DOM的样子
const cNode = {
tag:"div",//标签名 or 组件名
data:{
class:"red",//标签上的属性
on:{
click:() => {//事件
}
},
children:[//子元素们
{tag:"span",...},
{tag:"span",...}
],
...
}
}
//vue里面表示一个div,class为“red”,onclick
如何创建虚拟DOM
React.creatElement:
creatElement(
'div',//标签名
{classNmae.'red',onClick:() => {}},
[
creatElement('span',{//属性},'span1内容'),
creatElement('sapn',{},'sapn2内容')
]
)
//创建一个div里边有两个span
现在创建虚拟DOM方法—通过Babel 转为creatElement 形式
<div className="red" onClaick={fn}>
<sapn> span1内容 </span>
<sapn> span2内容 </span>
</div>
Vue(只能在render函数里得到h)
h('div',{
class:'red',
on:{
click: () => {}
},
},[h('span',{},'span1内容'),h('span',{},'span2内容')])
现在创建虚拟DOM方法—通过vue-loader 转为 h 形式
<div className="red" @claick="fn">
<sapn> span1内容 </span>
<sapn> span2内容 </span>
</div>
他有什么优点?
- 减少DOM操作
(1)虚拟DOM可以将多次操作并为一次操作,比如你添加1000个节点,却是一个接一个操作的。(减少DOM操作的次数)
(2)虚拟DOM借助 DOM diff算法 可以把多余的操作省掉,比如你添加1000个节点,其实只有10个是新增的。(减少DOM操作的范围) - 跨平台渲染
虚拟DOM不仅可以变成DOM,还可以变成小程序、iOS应用、安卓应用,因为虚拟DOM本质上只是一个JS对象。
他有什么缺点?
需要额外的创建函数,如createElement 或 h,但可以通过JSX来简化成XML写法。
DOM diff 虚拟DOM的对比算法
把虚拟DOM想象成树形
<div :class="x">
<sapn v-if="y"> {string1} </span>
<sapn> {string2} </span>
</div>
当数据变化时:
x从red变成green :
DOM diff 发现 :
- 标签类型没变,只需要更新div对应的DOM的属性;
- 子元素没变,不更行;
y 从 true 变成 false:
DOM diff 发现 : - div 没变,不用更新;
- 子元素1标签没变,但是children变了,更新DOM内容;
- 子元素2不见了,删除对应的DOM
他是什么?
就是一个函数,我们称之为patch;
patches= patch(oldVNode,newVNode)
paatches 就是要运行的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 (递归)
他有什么优缺点?
同级比较存在bug,会出现识别错误的问题,用:key="item.id"
解决。
推荐阅读
-
一篇文章带你搞懂Vue虚拟Dom与diff算法
-
深入理解Vue2.x的虚拟DOM diff原理
-
Vue的transition-group与Virtual Dom Diff算法的使用
-
react 虚拟DOM的diff算法
-
react——虚拟DOM的diff算法
-
WEB面试题_虚拟DOM与DOM Diff 的原理
-
闭包的原理与经典应用场景,访问器属性,类与对象的创建与成员引用,数组与对象的解构过程与经典案例,JS引入到浏览器中的的方法及获取DOM元素的两个API
-
OrgChart -- 基于纯 DOM 与 CSS3 的组织结构图插件_html/css_WEB-ITnose
-
闭包的原理与经典应用场景,访问器属性,类与对象的创建与成员引用,数组与对象的解构过程与经典案例,JS引入到浏览器中的的方法及获取DOM元素的两个API
-
OrgChart -- 基于纯 DOM 与 CSS3 的组织结构图插件_html/css_WEB-ITnose