Virtual Dom
什么是DOM
DOM就是文档对象模型,什么是文档对象模型?这就需要好好说说了。
HTML的文档document页面是一切的基础,没有它dom就无从谈起。
当创建好一个页面并加载到浏览器时,DOM就悄然而生,它会把网页文档转换为一个文档对象,主要功能是处理网页内容。
在这个文档对象里,所有的元素呈现出一种层次结构,就是说除了顶级元素html外,其他所有元素都被包含在另外的元素中。
假如有这么一段html代码:
1 | <html> |
那么它的树就应该是下面这样的一颗倒长的树。
一颗家谱树,而家谱树本身就是一种模型,其典型用法是表示表示人类家族谱系。
它很容易表明家族成员之间的关系,把复杂的关系简明地表示出来,因此这种模型非常适合表示一份html的文档:
文档对象模型就是基于这样的文档视图结构的一种模型所有的html页面都逃不开这个模型,也可以把它称为节点树更为准确。
什么是虚拟DOM
再来复习一下,什么是虚拟DOM。
虚拟DOM的定义是用一个javascript对象通过对象属性的方式去描述一个dom节点。
举个例子:
我们现在有一个a标签,如果用普通的写法是这样子的:
1 | <a href="http://xxx">链接</a> |
将a标签通过js对象来描述的话,是这样的:
1 | { tag: "a", attrs: { href: "http://xxx" }, text: "链接", children: []} |
一个真实的dom节点就这样通过js对象描述出来了。
为什么需要虚拟DOM?
我们可以看下a标签在浏览器下的输出,仅仅是一个空的a标签,就存在300+个属性。浏览器上面的真实DOM,除了一些基本的属性以外,还有很多为了支持标签本身特性而存在的很多属性和方法,这些属性和方法都是在浏览器底层去使用的,而对js来说真正有用的属性可能不到10个。
因此,前端很多时候对于dom操作都是极力避免的,如果确实需要,则希望将操作次数减到最少。由此产生了虚拟DOM,利用js去描述一个dom节点,只保留一些必要的、足够表达这个节点的属性。更新js对象比直接更新dom节点要节省很多的时间。
我们可以用js来模拟dom节点的更新,每当dom更新之后,我们先比对虚拟dom,找出需要更新的地方,然后最后再统一更新真实dom。
为什么DOM更新慢?
更新DOM
并不慢,就像更新任何JavaScript
对象一样。
那究竟是什么让更新真正的DOM
变慢?
是绘制。
布局过程中,绘制占用了大部分时间。
结合下图,以及此文章,你会明白,更新 DOM
的真正问题是屏幕的绘制。
负责在浏览器屏幕上显示或呈现网页的渲染引擎解析HTML
页面以创建DOM
。它还解析CSS
并将CSS
应用于HTML
,从而创建渲染树,此过程称为**attachment
**。
所以,当我们这样做时
1 | document.getElementById('elementId').innerHTML="New Value" |
发生以下事情:
- 浏览器必须解析
HTML
- 它删除了
elementId
的子元素 - 使用"New Value"更新
DOM
- 重新计算父和子的
CSS
- 更新布局,即每个元素在屏幕上的精确坐标
- 遍历渲染树并在浏览器显示上绘制它
重新计算CSS和更改布局使用复杂的算法,它们会影响性能。
因此,更新真正的DOM
并不仅仅涉及更新DOM
,而是涉及许多其他过程。
此外,上述每个步骤都针对真实DOM
的每次更新运行,即如果我们更新真实DOM
10次,则上述步骤中的每一个将重复10次。这就是为什么更新 DOM
很慢的原因。
Vue中的Virtual DOM
定义
我们打开vue源码文件的VNode类,这个类就是Vue用来描述真实dom节点的。
1 | export default class VNode { |
可以看到上面有一些常用的属性:tag表示标签名,text表示节点的文本内容,elm表示虚拟节点对应的真实dom节点等等。
分类
vue通过VNode可以描述6种节点类型:
- 注释节点
- 文本节点
- 克隆节点
- 元素节点
- 组件节点
- 函数式组件节点
diff算法
有兴趣的可以参考这篇文章:https://mp.weixin.qq.com/s/V2PLADamTyE4krSNuG6leQ
参考文章: