本文主要包含以下内容:
一、 虚拟DOM
- 虚拟DOM是什么
- 虚拟DOM的优点
- 虚拟DOM的缺点
二、 DOM diff
- DOM diff是什么
- DOM diff的大致逻辑
- DOM diff如何进行比较
- 解决DOM diff存在的问题
一、虚拟DOM
1. 虚拟DOM是什么?
1.1 DOM回顾
MDN对其解释如下:
事实上为了让网页实现各种各样的效果,浏览器给window添加了一个document对象,我们将网页抽象成一个document对象,通过这个document对象操作整个页面的所有的节点。上述模型Document Object Model
也就是DOM
。
我们每一次使用 js 操作 DOM,或者 DOM 每一次细微的改变,都会引发页面的重绘(paint)
和重排(layout)
,这也是我们经常所说的“DOM操作慢”的根本原因。
1.2 虚拟DOM
虚拟DOM,也叫VDOM
,一个能代表DOM树的对象,通常含有标签名、标签上的属性、事件监听和子元素以及其他属性。其本质是一个JS对象
.它是仅存于内存中的DOM,因为还未展示到页面中,所以称为VDOM。
举个随处可见的例子:
var a = document.createElement("div"); //这就是VDOM
那么如何让VDOM变成真实的DOM呢?
-简单,只需将节点append到页面中:
var a = document.createElement("div");
document.body.append(a); // 此时body内就会创建一个div标签,真实DOM
-
Vue、React中的虚拟DOM
- Vue
h('div', { class: 'red', on: { click: () => {} }, }, [h('span', {}, 'span1'), h('span', {}, 'span2')])
- React
createElement('div', {className:'red', onClick: () => {}}, [ createElement('span', {}, 'span1'), createElement('span', {}, 'span2') ] )
2.虚拟DOM的优点
2.1 减少DOM操作
-
减少DOM操作的次数,虚拟DOM可以将多次操作合并为一次操作,例如在div中添加1000个甚至更多的节点,常规的方法是一个接一个的操作的。
-
减少DOM操作的范围,虚拟DOM借助
DOM diff
(后文会说)可以把多余的操作去掉,例如添加1000个节点,实际上只有10个节点是新增的,DOM diff会将其简化操作,而不是重绘。
2.2 能够跨平台渲染
虚拟DOM不仅能够变成DOM,还可以变成小程序、ios应用、安卓应用,因为其本质是一个JS对象。
3. 虚拟DOM的缺点
需要通过额外的创建函数,如React
需要creatElement
,Vue
需要h
函数等,但是可以通过JSX语法(React)
、Vue-loader(Vue)
来解决。如下:
**Vue Template**
通过`Vue-loader`转化为`h`形式
<div class="red" @click="fn">
<span>span1</span>
<span>span2</span>
</div>
-----------------------------------------------
**React JSX**
通过`bable`转化`creatElemet`形式
<div className="red" onClick={fn}>
<span>span1</span>
<span>span2</span>
</div>
二、DOM diff
1. 什么是DOM diff
DOM diff两个虚拟DOM树对比的算法,diff 算法仅在两个树的同级的虚拟节点之间做比较,递归地进行比较,最终实现整个 DOM 树的更新。
DOM diff本质上就是一个函数,我们称之为patch
函数。有两个参数,分别是旧的虚拟DOM和新的虚拟DOM
patches=patch(oldVnode,newVnode)
patches
就是要运行的DOM操作,类似于:
[
{type: 'INSERT', vNode: ... },
{type: 'TEXT', vNode: ... },
{type: 'PROPS', propsPatch: [...]}
]
2. DOM diff的大致逻辑
-
tree diff(层级比较)
1.将新旧两棵树逐层对比,找出哪些节点需要更新。
2.如果节点是组件就看
Component diff
3.如果节点是标签就看
Element diff
-
Component diff
1.如果节点是组件,就先看组件类型,类型不同直接替换(删除旧的),类型相同则更新属性。
2.然后深入组件做
tree diff
-
Element diff
1.如果节点是原生标签,则看标签名,标签名不同直接替换,相同则更新属性。
2.然后进入标签后代做
tree diff
3.DOM diff如何进行比较
如下代码:
<div :class="x">
<span v-if="y">{string1}</span>
<span>{string2}</span>
</div>
以上代码树的结构图可用下图表示:
我们默认所有节点都是可变化的,假定div
中的class
变成了red
,于是patch函数就会开始遍历并进行比较。
下面我们再变化,将第一个span删除。
计算机会认为是第一个span更新了,第二个span删除了!这就是DOM diff所存在的问题。
4. 解决DOM diff存在的问题
那么有什么办法可以解决DOM diff存在的问题呢?答案就是使用key
(react跟vue都需要使用单独的key,这个key在同属于一个父节点的各兄弟节点上需要不同。)
如下:
//React JSX语法
<div>
{[{id:1,value:1},{id:2,value:2},{id:3,value:3}].map((v)=>{
return <span key={v.id}/>
})}
</div>
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!