前言
vue作为目前最热门的几个前端框架之一,我认为其比较突出的优点在于——本体非常的轻量、官方良好的教程、活跃的社区讨论、代码的易读性。最近接触到了其中的Virtual Dom应用,所以产生了为什么会出现Virtual Dom的疑问。
DOM
DOM
是文档对象模型(Document Object Model
)的简写,在html中定义的各种标签元素,都会生成一个dom节点,而这些dom节点会像一颗倒着的树一样,层层向下排列生成一颗dom树,这颗dom树存储了节点和节点之间的关系以及每个节点自身的值与属性。
我们可以通过js来修改dom节点,比如常见的新增节点,删除节点,插入节点等,这也就是我们常说的dom操作。但经常能够听到不要频繁的去操作dom节点的说法,因为这个操作在性能和时间上是昂贵的。
DOM操作为什么会很慢,从而使得操作dom的代价变得昂贵?
一直听说操作dom很花时间,代价比较高,但原因是什么,我一直不太清楚,这次刚好学习到Virtual Dom,于是就专门去研究了一下其中的门道。
首先我们可以明确一点,我们用js进行dom元素插入、删除、新增等dom操作,其实都是在操作dom对象,而dom对象本身也是一个js对象,而在js中操作js对象代价是很小的,所以操作dom对象本身并不是一个昂贵的操作,但是对这个对象的操作会触发一些浏览器的行为,正是这些被触发的浏览器行为导致操作dom会有很多额外的性能和时间花销,所以才会出现操作dom是一个昂贵操作的说法。
明确了这一点之后,我们再来看到底是什么浏览器行为增加了操作dom的负担呢?答案就是布局过程(layout)。
浏览器在渲染一个页面时,主要是通过浏览器本身的渲染引擎进行的,大致上有以下几步:
1. 获取html文档,解析并生成dom树
2. 读取样式,并将样式附着在dom树上,从而生成render树
3. 对render树上的样式属性进行计算,进行布局(layout),确定每个dom节点的布局信息,即该节点的大小、所处的位置等。
4. 根据布局信息在浏览器中渲染出整个页面
以上是一个简略的过程,主要为了展示布局(layout)的过程。从中不难看出,所谓的布局过程,就是将节点放在页面中他应该在的位置,并确定他的大小,颜色等样式属性。这个过程通常在页面刚开始渲染的时候会执行一次。之后每当我们在js里操作了一次dom节点,比如新增了一个dom节点,那么都将再次触发这个过程,重新计算新节点的样式属性,并调整页面中其它节点的位置,这将导致页面整体的重绘,这个过程是非常费时的,所以操作dom才会成为一个昂贵的操作。
Virtual Dom
那既然直接操作dom代价比较高,有什么办法可以让我们节省其中的成本,从而能够不用太过顾及操作dom的代价呢?这就是本文的主角Virtual Dom的作用。
顾名思义,虚拟dom有虚拟两字,也就说明了他不是真正的dom,那么我们操作这个虚拟dom,自然也就避开了直接操作实际dom的庞大成本。Virtual Dom到底是做了什么,从而让我们极大的降低了操作dom的成本呢?
首先,我们如果使用常规的js方法来操作dom,形式如下:
document.getElementById('myId').appendChild(myNewNode);
这是最常见的使用js添加dom节点的方式,通过调用document对象的方法,我们能够动态的操作dom结构,不过每次使用js去操作真实的dom对象,都可能触发布局进行页面的重绘,这个代价是很高的。
为了解决这个问题,我们使用手段克隆了一份dom树,也就是Virtual Dom,Virtual Dom可以看做一棵模拟了DOM树的JavaScript树,其主要是通过vnode,实现一个无状态的组件,当组件状态发生更新时,会触发Virtual Dom中数据的变化,然后通过Virtual Dom和真实DOM的比对,判断其中被修改的部分,然后将这些被修改的部分反馈到真实dom上。
可以简单认为Virtual Dom是真实DOM的缓存。即Virtual Dom是一个js对象,它复制了一份真实dom的结构,我们的dom操作不再是直接去操作真实dom树,而是操作的Virtual Dom,而对Virtual Dom的操作并不会触发浏览器的布局过程,当我们完成整个dom操作函数的上下文之后,Virtual Dom会将所有被修改的dom节点信息与真实dom进行对比,将这些修改信息一次性反馈到真实dom上。他们之间的区别可以这么理解,如果我们有十个dom操作,直接操作真实的dom树,会发生十次布局过程,进行十次的页面重绘,而如果是在Virtual Dom上进行操作,我们只是修改了十次js对象里的数据值,在修改完之后,Virtual Dom会把这十次修改反馈给真实dom,那么等于我们只操作了一次dom,进行一次布局重绘的过程。而且Virtual Dom还会计算出与真实dom的最小的差别,只反馈那些被修改的部分,从而极大的降低dom操作的成本。