虚拟DOM的性能测算

我们知道,Vue.js 要选择声明式的设计方案,原因就在于声明式代码的可维护性更强。但是声明式代码的性能不优于命令式代码的性能。

那么我们如何在保持可维护性的同时又能让性能损失最小化呢,这里就考虑到虚拟DOM。

命令式与声明式的文章中我讲过,
声明式代码的更新性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗。因此,如果我们能够最小化找出差异的性能消耗,就可以让声明式代码的性能无限接近命令式代码的性能。而所谓的虚拟 DOM,就是为了最小化 找出差异这一步的 性能消耗而出现的。

采用虚拟 DOM 更新技术的性能 理论上 不可能比原生 JavaScript 操作 DOM更高。这里我们强调了理论上,因为在大部分情况下,我们很难写出绝对优化的命令式代码,尤其是当应用程序的规模很大的时候,即使你写出了极致优化的代码,也一定耗费了巨大的精力,这时的投入产出比其实并不高。
那么,有没有什么办法能够让我们不用付出太多的努力(写声明式代码),还能够保证应用程序的性能下限,让应用程序的性能不至于太差,甚至想办法逼近命令式代码的性能呢?这其实就是虚拟 DOM 要解决的问题。

不过前文中所说的原生 JavaScript 实际上指的是像 document.createElement 之类的 DOM 操作方法,并不包含 innerHTML,因为它比较特殊,需要单独讨论。在早年使用 jQuery 或者直接使用 JavaScript 编写页面的时候,使用 innerHTML 来操作页面非常常见。

其实我们可以思考一下:

  1. 使用 innerHTML 操作页面和虚拟 DOM 相比性能如何?
  2. innerHTML 和document.createElement 等 DOM操作方法有何差异?
1,innerHTML 和虚拟 DOM 操作页面相比性能

为了比较 innerHTML 和虚拟 DOM 的性能,我们需要先了解它们创建、更新页面的过程。

1. 对于 innerHTML 来说,为了创建页面,我们需要构造一段 HTML 字符串:

01 const html = `
02 <div><span>...</span></div>
03 `

接着将该字符串赋值给 DOM 元素的 innerHTML 属性:

01 div.innerHTML = html

然而上面这句话远没有看上去那么简单。为了渲染出页面,首先要把字符串解析成 DOM 树,这是一个 DOM 层面的计算。我们知道,涉及 DOM 的运算要远比 JavaScript 层面的计算性能差,这有一个跑分结果可供参考,如图 所示:
在这里插入图片描述

  • 上边是纯 JavaScript 层面的计算,循环 10 000 次,每次创建一个 JavaScript 对象并将其添加到数组中;
  • 下边是 DOM 操作,每次创建一个 DOM 元素并将其添加到页面中。
    结果显示,纯 JavaScript 层面的操作要比 DOM 操作快得多,它们不在一个数量级上。

我们可以用一个公式来表达通过 innerHTML 创建页面的性能:HTML 字符串拼接的计算量 + innerHTML 的 DOM 计算量。

2, 虚拟 DOM 创建页面的过程分为两步:

  1. 是创建 JavaScript 对象,这个对象可以理解为真实 DOM 的描述;
  2. 递归地遍历虚拟 DOM 树并创建真实 DOM;

同样用一个公式来表达:创建 JavaScript 对象的计算量 + 创建真实 DOM 的计算量。
综上我们 直观地对比 innerHTML 和虚拟 DOM 在创建页面时的性能。
在这里插入图片描述
可以看到,无论是纯 JavaScript 层面的计算,还是 DOM 层面的计算,其实两者差距不大。这里我们从宏观的角度只看数量级上的差异。如果在同一个数量级,则认为没有差异。在创建页面的时候,都需要新建所有 DOM 元素。

刚刚只是讨论了创建页面时的性能情况,大家可能会觉得虚拟 DOM 相比 innerHTML 没有优势可言,甚至细究的话性能可能会更差。别着急,接下来我们看看它们在更新页面时的性能。

使用 innerHTML 更新页面的过程是重新构建 HTML 字符串,再重新设置 DOM 元素的 innerHTML 属性,这其实是在说,哪怕我们只更改了一个文字,也要重新设置 innerHTML 属性。而重新设置 innerHTML 属性就等价于销毁所有旧的DOM 元素,再全量创建新的 DOM 元素。
再来看虚拟 DOM 是如何更新页面的。它需要重新创建 JavaScript 对象(虚拟DOM 树),然后比较新旧虚拟 DOM,找到变化的元素并更新它。如下图所示:
在这里插入图片描述
可以发现,在更新页面时,虚拟 DOM 在 JavaScript 层面的运算要比创建页面时多出一个 Diff 的性能消耗,然而它毕竟也是 JavaScript 层面的运算,所以不会产生数量级的差异。再观察 DOM 层面的运算,可以发现虚拟 DOM 在更新页面时只会更新必要的元素,但 innerHTML 需要全量更新。这时虚拟 DOM 的优势就体现出来了。

另外,当更新页面时,影响虚拟 DOM 的性能因素与影响 innerHTML 的性能因素是不同。对于虚拟 DOM 来说,无论页面多大,都只会更新变化的内容,而对于 innerHTML 来说,页面越大,就意味着更新时的性能消耗越大。如果加上性能因素,那么最终它们在更新页面时的性能如图所示:
在这里插入图片描述
基于此,我们可以粗略地总结一下 innerHTML、虚拟 DOM 以及原生 JavaScript(指 createElement 等方法)在更新页面时的性能,如图所示。
在这里插入图片描述
我们分了几个维度:心智负担、可维护性和性能。

  • 原生 DOM 操作方法的心智负担最大,因为需要手动创建、删除、修改大量的 DOM元素。但它的性能是最高的,不过为了使其性能最佳,我们同样要承受巨大的心智负担。另外,以这种方式编写的代码,可维护性也极差。
  • 对于 innerHTML 来说,由于我们编写页面的过程有一部分是通过拼接 HTML字符串来实现的,这有点儿接近声明式的意思,但是拼接字符串总归也是有一定心智负担的,而且对于事件绑定之类的事情,我们还是要使用原生JavaScript 来处理。如果 innerHTML 模板很大,则其更新页面的性能最差,尤其是在只有少量更新时。
  • 虚拟 DOM,它是声明式的,因此心智负担小,可维护性强,性能虽然比不上极致优化的原生JavaScript,但是在保证心智负担和可维护性的前提下相当不错。

至此,我们有必要思考一下:有没有办法做到,既声明式地描述 UI,又具备原生 JavaScript 的性能呢?看上去有点儿鱼与熊掌兼得的意思,这可以通过 编译时 + 运行时 的架构来解决。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
虚拟DOM(Virtual DOM)是一种在内存中用 JavaScript 对象表示网页的 DOM 结构的技术。通过使用虚拟DOM,可以在保持页面功能不变的同时,提高网页性能的主要原因如下: 1. 批量更新:虚拟DOM能够将多个DOM操作批量处理,减少了直接操作实际DOM的次数。通过比较虚拟DOM树和实际DOM树的差异,只对需要更新的部分进行实际DOM操作,从而减少了浏览器的重绘和回流次数。 2. 减少重绘和回流:虚拟DOM可以通过比较更新前后的虚拟DOM树差异,只更新需要变化的部分。这种优化方式可以减少浏览器对页面进行重绘和回流的次数,从而提高网页性能。 3. 提高渲染效率:虚拟DOM可以在内存中进行操作,而不是直接操作实际DOM。在虚拟DOM中进行各种计算和操作要比直接在实际DOM中进行快得多。这样可以提高页面的渲染效率,减少用户在页面上的等待时间。 4. 跨平台支持:虚拟DOM是对实际DOM的抽象表示,在不同平台上运行的应用程序可以共享相同的虚拟DOM操作逻辑。这种跨平台支持使得开发者能够更轻松地开发和维护多平台的应用程序。 需要注意的是,虚拟DOM并不一定总是能够提高网页性能。在某些情况下,虚拟DOM的额外计算和比较操作可能会带来一定的性能开销。因此,在实际应用中,仍然需要根据具体情况综合考虑使用虚拟DOM的优缺点,并进行性能测试和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值