虚拟DOM简介

什么是虚拟DOM?

我们现在使用的三大主流框架Vue.js、Angular和React都是声明式操作DOM。我们通过描述状态和DOM之间的映射关系是怎样的,就可以将状态渲染成试图。关于状态到视图的转化过程,框架会帮我们做,不需要我们自己去操作DOM。
状态可以是JavaScript中的任意类型。Object、Array、String、Number、Boolean等都可以作为状态,这些状态可能最终会以段落、表单、链接或按钮等元素呈现在用户界面上。
本质上,我们将状态作为输入,并生成DOM输出在页面上显示出来,这个过程叫做渲染


渲染的过程.PNG

然而通常在程序运行时,状态会不断发生改变(状态改变的原因有很多,可能是用户点击了某个按钮,可能是某个ajax请求,这些行为都是异步的)每当状态发生变化时,都需要重新渲染。如何确定状态中发生了什么变化以及需要在哪里更新DOM?
在这种情况下,最简单粗暴的方式是,不需要关心状态发生了什么变化,不需要关心哪里更新DOM,我们只要把所有DOM删除了,然后使用状态重新生成一份DOM,并将其输出到界面上。
但是访问DOM是非常昂贵的,按照上面的方式,会造成相当多的性能浪费。状态变化通常只是有限的几个节点需要重新渲染,所有我们不仅需要找出哪里需要更新,还需要尽可能少的访问DOM。


状态发生变化.PNG

如上图所示,当某个状态发生变化时,只更新与这个状态相关联的DOM节点。
这个问题有很多种解决方案,目前,各大主流框架都有自己一套解决方案,在Angular中就是脏检查的流程,React中使用虚拟DOM,vuejs1.0通过细粒度的绑定。因此,虚拟DOM本质上只是众多解决方案中的一种,可以用但并不一定必须用。
虚拟DOM的解决方式是通过状态生成一个虚拟节点树,然后使用虚拟节点树进行渲染。在渲染之前,会使用新生成的虚拟节点数和上一次生成的虚拟节点树进行对比,只渲染不同的部分。
虚拟节点数其实是由组件树建立起来的整个虚拟节点(Virtual Node,也简写为Vnode)树。


虚拟节点树.PNG

为什么要引入虚拟DOM

事实上,Angular和React的变化侦测有一个共同点,那就是他们都不知道哪些状态变了。因此,就需要进行比较暴力的对比,React是通过虚拟DOM的比对,Angular是使用脏检查的流程。
Vue.js的变化侦测不一样,它在一定程度上知道具体哪些状态发生了变化,这样就可以通过更细粒度的绑定来更新视图。也就是说,在Vue.js中,当状态发生变化时,它在一定程度上知道哪些节点使用了这个状态,从而对这些节点进行更新操作,不需要对比。事实上,在vue.js 1.0中就是这样实现的。
但是这样做也有一定的代价,因为粒度太细,每一个绑定都会有一个对应得watcher来观察状态的变化,这样就会有一定的内存开销和追踪依赖的开销。当状态被越多的节点使用时,开销就越大。大型项目来说,这个开销是非常大。
因此,Vue.js 2.0中选择了中等粒度的解决方案,那就是引入了虚拟DOM。组件级别是一个watcher实例,就是说即便一个组件内有10个节点使用了某个状态,但其实也只有一个watcher在观察这个状态的变化。所以这个状态发生变化时,只能通知到组件,然后组件内部通过虚拟DOM去进行比对和渲染。

Vue.js 中的虚拟DOM

在vue.js中,我们使用模板来描述状态和DOM之间的映射关系。Vue.js通过编译将模板转化为渲染函数render,执行渲染函数就可以得到一个虚拟节点树,使用这个虚拟节点树就可以渲染页面。


模板转化为视图.PNG

虚拟DOM的终极目标是将虚拟节点(vnode)渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会造成很多不必要的DOM操作。
例如一个ul标签下有很多li标签,其中只有一个li变化,这种情况下如果直接用新的ul替换旧的ul,其实除了那个发生了变化的li节点之外,其他节点都不需要重新渲染。
由于DOM操作比较慢,所以这些DOM操作在性能上会有一定的浪费。避免这些不必要的DOM操作会提升很大的性能。
为了避免不必要的DOM操作,虚拟DOM在虚拟节点映射到视图的过程中,将虚拟节点和上一次渲染视图所使用的的旧虚拟节点(oldVnode)进行对比。找出真正需要更新的节点来进行DOM操作,可以避免不必要改动的DOM。
图中给出了虚拟DOM的整体运行流程,先将vnode和oldVnode做对比,然后在更新视图


虚拟DOM执行过程.PNG

可以看出虚拟DOM在Vue.js中所做的事情并没有那么复杂,他主要做了两件事
  • 提供与真实DOM节点所对应得虚拟节点vnode
  • 将虚拟节点vnode和旧虚拟节点oldvnode进行对比,然后更新视图。
    vnode是JavaScript中一个很普通的对象,这个对象的属性上保存了生成DOM节点所需要的一些数据。
    对比两个虚拟节点是虚拟DOM中最核心的算法(即patch),他可以判断出哪些节点发生了变化,从而只对发生了变化的节点进行操作。

总结

虚拟DOM是讲状态映射成试图的众多解决方案之一,它的运作原理是使用状态生成虚拟节点,然后使用虚拟节点渲染成视图。
之所以需要先使用状态生成虚拟节点,是因为如果直接用状态生成真实的DOM,会有一定程度上的性能浪费。而先创建虚拟节点再渲染视图,就可以将虚拟节点缓存,然后使用新创建的虚拟节点和上一次缓存的虚拟节点进行对比,然后根据对比结果更新需要更新的DOM节点,避免不必要的DOM操作。
由于Vue.js的变化侦测粒度更细,所以挡状态发生变化时,vue.js知道的信息更多,一定程度上知道哪些位置使用了窗台。因此,vue.js可以通过细粒度的绑定来更新视图,vue.js 1.0 就是这样实现的。
但是这么做也有一定的代价。因为粒度太细,就会有很多的watcher同时观察这些状态,会有一定的内存开销和依赖追踪依赖的开销,所以vue.js 2.0 采取了中等粒度的解决方案。状态侦测不再是某个具体节点,而是某个组件,组件内部通过虚拟DOM来渲染视图,这样可以大大的缩减依赖数量和watcher数量。
Vue.js中通过模板来描述状态和视图之间的映射关系,所以会将模板编译成渲染函数render,然后执行渲染函数生成虚拟节点vnode,最后使用虚拟节点更新视图。
虚拟DOM在vue.js中所做的事是将虚拟节点vnode和旧虚拟节点oldVnode进行对比,根据对比结果来进行DOM操作来更新视图。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容