使用 JavaScript 高效地执行多个 DOM 更新

新手上路,请多包涵

我有一个包含数百个 div 元素的 HTML/JS 网站。这些元素中的几十个应该一次快速更新(每秒最多 250 次)(即它们应该一次全部更新,而不是让浏览器通过逐个执行更新来执行不必要的工作 -一)。什么是最有效的方式(最小化 CPU 负载和内存消耗)来支持现代浏览器使用纯 JavaScript 或简单的库(不使用 React 或类似的库,要求我修改 DOM 处理之外的代码) )?我正在寻找这样的东西(其中 imaginaryLibrary 是我不知道的图书馆,我正在寻找):

 var i, element;
for(i = 0; i < 20; i++){
    element = document.getElementById('el' + i);
    imaginaryLibrary.collectDomUpdate(element, cssClass, 'updatedCssClass');
}
//executes DOM updates collected in loop before and applies the DOM updates in one reflow
imaginaryLibrary.applyUpdates();

要更新的元素的父元素包含数千个不应更新的元素。

原文由 simon 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 786
1 个回答

你并没有真正具体地说明你在做什么,所以我们在这里能做的最好的就是给你一些相关领域的一般性建议。

DOM 修改

您并没有完全填写您要执行的操作的所有细节,但是在优化代码的工作方式时需要考虑以下几点:

  1. 回流已经排队。 浏览器已经尝试最小化回流,因此如果您在一段连续的 Javascript 中进行四次 dom 修改,浏览器将等待该段 Javascript 完成运行,然后进行一次回流和一次重绘。

  2. 请求某些属性可以触发立即回流。 您要确保避免上述规则的例外情况。例如,如果您请求某些 DOM 属性需要正确布局才能准确报告属性值,并且存在来自先前修改的待定布局,则浏览器可能会在返回您请求的属性之前同步重新布局文档。这些类型的属性通常涉及屏幕位置和其他明显受文档布局影响的属性。如果您想查找更多详细信息,有很多关于此主题的文章。在许多情况下,您的代码无论如何都不会使用这些属性,但如果是这样,通常的解决方法是在对 DOM 进行任何更改之前先请求所有需要的属性。

  3. 一次批处理所有 DOM 更改。 最糟糕的事情是更改 DOM,用计时器等待几毫秒,再更改一次 DOM,用计时器等待几毫秒等等,因为你将有 DOM 更改、回流、重绘、DOM 更改、回流、重绘等… 相反,请确保您在一段同步的 Javascript 中同时执行所有未决的 DOM 更改。这将允许浏览器对回流和重绘进行排队,并且在您完成所有 DOM 更改后只执行一次。如果您想更智能地进行批处理,您将折叠对同一元素的修改,以便它只用最终值处理一次。因此,如果 elementA 首先被赋予一个新值 3,然后在同一批次中被赋予一个值 4,那么在处理这批数据时,您希望跳过 3 而只处理 4。

  4. 有时可以优化 DOM 修改。 你没有给我们任何关于如何修改 DOM 的细节,但是如果你正在修改复杂的对象(比如添加许多表格行或改变很多表格单元格),那么有时创建一组 DOM 元素会更好当前未插入到 DOM 中,对它们进行所有修改,然后通过一个操作将它们插入到 DOM 中。这是因为修改当前插入 DOM 中的 DOM 元素会强制浏览器找出哪些其他 DOM 元素受此更改影响,以便它可以排队适当的回流或重绘。但是修改屏幕外 DOM 元素不需要做任何这些工作,直到最后一步将一个更大的对象插入到 DOM 中。这里没有有用的通用规则,因为您如何利用此优化完全取决于您正在进行的 DOM 修改。我们可以帮助您解决这个问题,但前提是我们可以看到您拥有的 HTML 以及您对它所做的确切更改。

更新时间

现在,关于事情的时间,你提到了每秒 250 次。对于用户可见的东西来说,这有点快(每次操作 4 毫秒)。如果你这样做,你的浏览器基本上会不断回流和重绘,只是偶尔暂停以处理其他用户事件。由于没有用户能真正看到从现在开始的 4 毫秒、8 毫秒和 12 毫秒发生的事情,所以真的没有必要经常更新屏幕上的状态。

因此,如果您确实有如此快或如此频繁的更改,您可能希望通过将它们累积到本地数据结构中来对它们进行批处理,然后每 100-500 毫秒左右更新一次屏幕。您必须试验可以使用的长间隔,并且不会真正注意到任何延迟。

批处理更新有几种实现策略。如果您的修改总是不断流入,我能想到的最简单的方法是在修改进入时将修改放入本地数组,然后为您的更新间隔设置一个间隔计时器来检查数组以及其中是否有任何内容,它将数组中的所有项目处理为 DOM 更新。

从服务器获取更改

您没有提到浏览器 Javascript 如何获取新数据。一般有两种选择,重复Ajax请求或者创建一个webSocket,服务器随时可以直接给你发送数据。

如果您的更新有规律且间隔较短,则 webSocket 连接显然是最有效的。这是一个持续的连接,只要服务器有新数据要发送,服务器就可以向每个客户端发送新数据。

如果您要使用 Ajax 进行轮询,那么我强烈建议您延长轮询间隔。非常短的轮询间隔会真正加载您的服务器并耗尽客户端的电池。

电池

如果这个应用程序打算在电池供电的设备上运行很长时间,那么从任何服务器实时(例如,每隔几毫秒)获取稳定的数据流都会消耗电池,因为收音机(WiFi或蜂窝)将几乎一直处于活动状态,CPU 也将运行很多。

原文由 jfriend00 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题