但是为什么浏览器 DOM 经过 10 年的努力仍然如此缓慢?

IT技术 javascript dom
2021-03-19 14:28:17

Web 浏览器 DOM 自 90 年代后期就已经存在,但它仍然是性能/速度方面的最大限制之一。

我们有来自 Google、Mozilla、Microsoft、Opera、W3C 和其他各种组织的一些世界上最聪明的人才为我们所有人研究 Web 技术,所以显然这不是一个简单的“哦,我们没有优化它“ 问题。

我的问题是,如果我要在专门处理此问题的 Web 浏览器部分工作,为什么我会很难让它运行得更快?

我的问题不是是什么让它变慢,而是问为什么它没有变得更快?

这似乎与其他地方正在发生的事情背道而驰,例如性能接近 C++ 代码的 JS 引擎。

快速脚本示例:

for (var i=0;i<=10000;i++){
    someString = "foo";
}

由于 DOM 导致缓慢的示例:

for (var i=0;i<=10000;i++){
    element.innerHTML = "foo";
}

根据要求的一些细节:

基准测试后,看起来不是一个无法解决的慢问题,但经常使用错误的工具,使用的工具取决于您跨浏览器在做什么。

貌似不同浏览器的 DOM 效率差别很大,但是我原来认为 dom 慢且无法解决的假设似乎是错误的。

我对 Chrome、FF4 和 IE 5-9 进行了测试,您可以在此图表中看到每秒的操作数:

在此处输入图片说明

当您使用 DOM API 时,Chrome 快如闪电,但使用 .innerHTML 操作符时要慢得多(慢 1000 倍),但是,FF 在某些方面比 Chrome 差(例如,附加测试比Chrome),但 InnerHTML 测试的运行速度比 Chrome 快得多。

从 5.5 开始,IE 似乎在使用 DOM append 方面变得更糟,而在 innerHTML 方面变得更好(即,IE8 中的 73ops/sec 现在 IE9 中的 51 ops/sec)。

我这里有测试页:

http://jsperf.com/browser-dom-speed-tests2

有趣的是,似乎不同的浏览器在生成 DOM 时都面临着不同的挑战。为什么这里会有这样的差距?

3个回答

当您更改 DOM 中的某些内容时,它可能会产生无数与重新计算布局、样式表等有关的副作用。

这不是唯一的原因:当您设置时,您element.innerHTML=x不再处理普通的“在此处存储值”变量,而是处理特殊对象,这些对象在您设置它们时会更新浏览器中的内部状态负载。

的全部影响element.innerHTML=x是巨大的。粗略概述:

  • 解析x为 HTML
  • 向浏览器扩展请求许可
  • 销毁现有的子节点 element
  • 创建子节点
  • 重新计算根据父子关系定义的样式
  • 重新计算页面元素的物理尺寸
  • 将更改通知浏览器扩展
  • 更新作为真实 DOM 节点句柄的 Javascript 变量

所有这些更新都必须通过一个连接 Javascript 和 HTML 引擎的 API。如今 Javascript 如此之快的一个原因是我们将其编译为某种更快的语言甚至机器代码,由于值的行为是明确定义的,因此会发生大量优化。通过DOM API工作时,没有 这种可能。其他地方的加速已经把 DOM 抛在了后面。

首先,您对 DOM 所做的任何事情都可能是用户可见的更改。如果您更改 DOM,浏览器必须重新布置所有内容。它可能会更快,如果浏览器缓存更改,然后只布置每 X 毫秒(假设它还没有这样做),但也许对这种功能的需求并不大。

其次,innerHTML 不是一个简单的操作。这是微软推动的一个肮脏的黑客,其他浏览器也采用了它,因为它非常有用;但它不是标准 (IIRC) 的一部分。使用innerHTML,浏览器必须解析字符串,并将其转换为DOM。解析很难。

原始测试作者是 Hixie ( http://nontroppo.org/timer/Hixie_DOM.html )。

这个问题已经在StackOverflowConnect (bug-tracker)上讨论过使用 IE10,问题已解决。通过已解决,我的意思是他们已经部分地转向了另一种更新 DOM 的方式。

IE 团队似乎处理 DOM 更新类似于 Microsoft 的 Excel 宏团队,在那里更新工作表上的活细胞被认为是一种糟糕的做法。您,开发人员,应该将繁重的任务离线,然后批量更新实时团队。在 IE 中,你应该使用文档片段(而不是文档)来做到这一点。随着新出现的 ECMA 和 W3C 标准,文档碎片被贬值。所以 IE 团队已经做了一些很好的工作来解决这个问题。

他们花了几周时间才将其从 IE10-ConsumerPreview 中的 ~42,000 毫秒减少到 IE10-RTM 中的 ~600 毫秒但是花了很多时间才说服他们这是一个问题。他们声称没有真实世界的例子每个元素有 10,000 次更新。由于无法预测富互联网应用程序 (RIA) 的范围和性质,因此其性能接近联盟中的其他浏览器至关重要。这是 OP 在 MS Connect 上对 DOM 的另一种看法(在评论中):

当我浏览到http://nontroppo.org/timer/Hixie_DOM.html 时,它需要约 680 毫秒,如果我保存页面并在本地运行它,则需要约 350 毫秒!

如果我使用 button-onclick 事件来运行脚本(而不是 body-onload),也会发生同样的事情。比较这两个版本:

jsfiddle.net/uAySs/ <-- 主体加载

对比

jsfiddle.net/8Kagz/ <-- 按钮点击

几乎2倍的差异..

显然,onload 和 onclick 的底层行为也各不相同。在未来的更新中它可能会变得更好。