关于 Virtual DOM 和直接 DOM 操作哪个性能更高?

网上关于 Virtual DOM(下称VDOM) 和 直接操作 DOM 来更新页面谁的性能更高有许多争论。这里就 VDOM 的性能问题简单谈下自己的理解。

一、什么是 VDOM

VDOM 本质上是一个 Javascript 对象,用来描述 DOM 结构,如:

<html>
    <head></head>
    <body>
        <ul class="list-ul">
            <li class="list-one">List One</li>
        </ul>
    </body>
</html>

可以用如下对象表示:

const vdom = {
    tagName: "html",
    children: [
        { tagName: "head" },
        {
            tagName: "body",
            children: [
                {
                    tagName: "ul",
                    attributes: { "class": "list-ul" },
                    children: [
                        {
                            tagName: "li",
                            attributes: { "class": "list-one" },
                            textContent: "List One"
                        }
                    ]
                }
            ]
        }
    ]
}

在实际的生产环境需要将这个 JS 对象转化成真实的 DOM 元素。

二、Js 操作 DOM 的开销

1、浏览器渲染引擎工作流程大致分为如下步骤:

  • 解析 HTML 创建 DOM 树
    当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
  • 解析 CSS 创建 CSS 规则树
  • 合并 DOM 树和 CSS规则,生成 render 树
  • Layout / Reflow:布局 render 树,计算各个元素的尺寸和位置等
  • Paint:绘制页面内容

2、JS 频繁操 DOM 的开销:

  • 在上述渲染过程中,前3点可能要多次执行,比如 Js 操作 DOM、更改 CSS 样式时,浏览器又要重新构建 DOM、CSS 规则树,重新 render,重新 Layout、Paint;
  • Layout 在 Paint 之前,因此每次 Layout 重新布局(Reflow 回流)后都要重新 Paint / 渲染,这时又要去消耗 GPU 资源
  • Paint 不一定会触发 Layout,比如颜色、背景的改变,只需要 Repaint / 重绘
  • 图片下载完也会重新出发 Layout 和 Paint
  • 虽然浏览器针对渲染流程有优化,但是这个过程开销还是巨大的。

三、为什么需要 VDOM

假如在实际生产环境中,有这么一个列表:

<ul>
    <li><span>item 1</span></li>
    <li><span>item 2</span></li>
    <li><span>item 3</span></li>
    ...
    <li><span>item 100</span></li>
</ul>

我们现在需要更新列表的,从后端拿到了数据,但是新的数据只有第50行的数据有变化。
1、最简单最节约心智的办法是,我们不关心新数据与老数据之间的差异,直接 innerHTML 更新整个 ul 标签里的内容。但是这样就造成了不必要的 DOM 操作开销,毕竟只需要更新一个节点,但是为了省事,99次操作是浪费资源的无用功。
2、或者我们逐个对比数据,发现是第50行有变化,直接将第50行的 span 用 innerText 更新内容即可。
3、虽然手动更新第50行内容达到了最小操作,但是每次从后端拿到新数据,我们并不能都知道是哪些行数据有变化,这个时候就需要写一个通用的方法来比较新旧数据的变化,并只去更新数据有变化的节点。
但是这样还不够,这个通用方法也只是满足了这一个列表的数据对比和节点更新,如果我们的项目中还有几十、几百个其它的列表呢?
这个时候 VDOM 就派上用场了。我们把整个页面抽象成 Js 对象(VDOM),每次的更新数据,我们都先更新 VDOM,再通过比较 新旧 VDOM 的变化,找到具体要更新的节点,再去操作具体的 DOM。
这个时候可能有人要问了,那在更新 VDOM 的时候不也做了很多无用功的操作吗?
对,但是 VDOM 的操作都是纯 Js 的计算,大家要明确的一点是 Js 计算(特别是在 V8 引擎的加持下)要比真实 DOM 操作开销小得多,最重要的是再也不用操心到底哪些数据有更新了,直接无脑用 VDOM 就是,开发效率提高了!

四、那到底谁的效率更高

用这个列表举例:

<ul>
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
    ...
    <li>item 100</li>
</ul>

1、当列表新数据更新了第50、60行时:

  • 手动对第50、60行的 span 节点进行 innerText操作,这是性能最高的,我们计它的耗时为 T_1
  • 直接 innerHTML 整个 ul 列表,不考虑浏览器渲染优化等情况,简单的认为会多出88次节点操作,计为 T_2
  • 通过 VDOM 来更新 DOM :
    抽象 DOM 结构为 VDOM -> Diff 策略比较变化 -> 更新真实 DOM
    则耗时为 Js 计算 VDOM 变化(T_3)与最小节点操作(T_1)之和,计为: T_1 + T_3

前面也说了得益于现代浏览器的高效,Js 计算是非常快的,故 T_1 + T_3 << T_2
综合比较三种更新方式,T_1 < T_1 + T_3 << T_2
可以看出手动去进行最小节点操作是性能最好的,但是其心智负担也不小。

2、当 ul 中所有数据都变了,那就能直接无脑 innerHTML 进行更新,因为此时更新所有节点就是最小节点操作。
所以一个项目能确定基本上每一页的内容都不相同,几乎要全部更新,那可以不用 VDOM,直接用 innerHTML 即可。
反之一个项目,有很多列表,有众多增删改查操作,那用 VDOM 是十分有意义的。

所以抛开场景谈性能就如同抛开剂量谈毒性,都是耍流氓。用 VDOM 更新真实 DOM 其实是在节约开销(运行效率)和节约心智(省事、提高开发效率)之间找到一个比较好的平衡点。不然为什么人家 React 和 Vue 要用 VDOM 呢?成千上万人验证过的东西,能流行一定有它的道理

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

推荐阅读更多精彩内容