现代浏览器一次可以处理多少个HTML元素?

35

“现代”是因为这个定义可能随着时间的推移而改变(特指桌面浏览器)

“处理”是因为这可能会根据机器配置/内存而有所不同,但特指一般使用情况。


这个问题是针对我正在解决的一个涉及大型数据集的特定问题而提出的。基本上,每当对特定数据集进行更改时,我都会收到完整的数据集,并且必须在浏览器中呈现此数据。
例如,在websocket上,我会收到一个推送事件,告诉我数据集有更改,然后我必须通过获取现有DOM元素、复制它、使用类名或其他元素标识符填充元素中的数据,并将其添加回DOM来呈现此数据集的HTML。
请注意,此数据集中的任何对象(JSON)可能有多达1000个以上的子对象,并且可能有多达10,000个以上的父对象,因此您可以看到返回的数据集可能向上达到1,000,000 => 10,000,000个数据点或更多。
现在有趣的部分是当我必须呈现这些东西时。对于每个数据点,可能会使用3或4个标记来呈现和样式化数据,并且可能会为其中任何标记设置事件侦听器(也许在父容器上使用委托减轻负担)。
总而言之,可能会有大量需要渲染的传入信息,我正在尝试找出处理此情况的最佳方法。理想情况下,您只需要为发生更改的单个数据点呈现更改,而不是重新呈现整个集合,但由于后端的设计方式,这可能不是一个选项。我的主要关注点是了解浏览器/DOM的限制,并通过前端的视角来看待这个问题。确实应该在后端进行一些更改(数据设计、缓存、分页),但这不是重点。这不是HTML/DOM的典型用例,因为我知道它们有限制,但它们到底是什么?我们仍然被限制在大约3000-4000个元素吗?
我有一些关于这个问题的相关 子问题,我正在积极地 寻找 答案。但我认为与stackoverflow社区分享一些想法并尝试汇集一些关于这个问题的信息会很好。 现代浏览器可以处理多少个DOM元素才能保持良好的性能? 如何对浏览器可以处理的DOM元素数量进行基准测试?

除了分页之外,处理需要呈现的大型数据集的一些策略是什么?

对于从数据/JSON渲染HTML(在前端)来说,像mustache和handlebars这样的模板框架是否比使用jQuery或正则表达式更具性能?


4
现在有一些“现代化”的浏览器可以在功能强大的台式机上运行,也有一些“现代化”的浏览器可以在廉价的智能手机上运行。客户端系统的能力将成为限制因素。 - Pointy
4
如果数据集达到了1,000,000个数据点,你应该重新考虑这个概念,并且一次只输出其中的一小部分,因为没有用户愿意在同一页上浏览1000万个元素,或者至少我认为是这样的。 - adeneo
1
此外,DOM 的一个方面是允许相同的元素被重复使用,例如 HTML5 Canvas。DOM 应该保持动态,并且“对象”应该一直保存在数据存储区中,直到需要使用它们。在浏览器中达到某种关键元素负荷之前,更可能受到糟糕代码和内存泄漏的限制。 - tremor
1
@adeneo 我承认数据设计存在问题,但这并不是实质性的问题。有很多后端策略可以解决这个问题,但我想专注于前端解决方案/限制。 - qodeninja
1
@Pointy,我更关注于尝试理解前端/客户端与DOM的限制,而不是真正地尝试解决问题,这只是为了说明。 - qodeninja
显示剩余6条评论
5个回答

10
您的答案是:1或数百万。我将复制/粘贴一个类似问题的答案,来自SO。
“老实说,如果您真正需要这个问题的绝对答案,那么您可能需要重新考虑您的设计。没有正确的答案,因为它取决于许多特定于应用程序的因素。例如,CSS使用较多还是较少,div的大小,每个div所需的实际图形渲染数量,目标浏览器/平台,DOM事件监听器的数量等。仅因为您可以这样做并不意味着您应该这样做! :-)”
请参见:有多少个 div 可以使 DOM 减慢并变得不稳定?
这确实是一个无法回答的问题,有太多的因素需要考虑。但是我想说,在单个页面加载的情况下,我使用了1毫秒的javascript setinterval来不断向页面添加新的div,ID递增1。我的Chrome浏览器刚刚超过了20,000,使用了600MB的RAM。

在我之前对这个问题的探索中,浏览器似乎在3000-4000个dom元素左右悬停,然后内存就会超载。一定有一种方法可以对此进行基准测试,但我还没有找到任何明显的东西。 - qodeninja
这真的取决于DOM元素中包含了什么,我只是添加了空的div。当它接近1GB内存时,我刚刚关闭了浏览器以避免崩溃,它几乎达到了40k。我也使用的是vPro I7 CPU。 - tremor
说得好。由于有许多因素,这个问题类似于“一辆车能开多快”。虽然我要说,这种问法结合不同浏览器的测试以及关于现代桌面硬件合理期望的调查信息,会提供一些有成果的结果。 - Jake
1
我使用名为2DX的框架,将一个包含100万个TD元素和4个字符字符串的表格加载。浏览器内存增加了2.2 GB。如果使用tremor的每20,000个600mb,总计需要30 GB。链接是http://js2dx.com。 - Gonki
@Gonki,你的假设非常错误,即内存使用量和DOM元素之间存在直接线性数学关系。实际上并不存在这种关系。 - tremor
3
@tremor,有一种直接关系,但并非完全线性。关系的系数取决于具体实现。根据我在一个特定浏览器上的测试结果,事实上它几乎是线性的。你可以详细解释一下吗? - Gonki

7

这是一个只有统计学专家才能给出准确全面答案的问题。

为什么

适当的方程式如下,其中N是节点数,bytesN是在DOM中表示它们所需的总字节数,节点索引范围为n∈[0,N),bytesOverhead是使用绝对最小属性配置和没有innerHTML的节点所使用的内存量,而bytesContent是用于填充此类最小节点所使用的内存量。

bytesN = ∑N (bytesContentn + bytesOverheadn)

问题中请求的值是在最坏情况下手持设备、操作系统、浏览器和操作条件下的N的最大值。对于每个排列解决N并不是微不足道的。上述方程揭示了三个依赖关系,每个依赖关系都可能严重改变答案。

依赖关系

  1. 节点的平均大小取决于每个节点中用于保存内容(例如UTF-8文本、属性名称和值或缓存信息)的平均字节数。
  2. DOM对象的平均开销取决于管理每个文档的DOM表示的HTTP用户代理。W3C的文档对象模型常见问题解答指出,“虽然所有DOM实现都应该是互操作的,但它们在代码大小、内存需求和单个操作的性能方面可能有很大的差异。”
  3. 可用于DOM表示的内存取决于默认使用的浏览器(这可能因手持设备供应商或用户而异)、用户覆盖默认浏览器、操作系统版本、手持设备的内存容量、常见后台任务和其他内存消耗。

严格解决方案

可以对常用的手持设备上使用的每个http用户代理进行测试以确定(1)和(2)。任何给定站点的用户代理分布可以通过配置Web服务器的日志记录机制来获取,以便将HTTP_USER_AGENT放置在默认情况下没有该字段的位置,然后在日志中剥离除该字段外的所有内容并计算每个值的实例数。

需要针对属性值和UTF-8内部文本(或其他编码)测试每个字符的字节数,以获得用于计算(1)的明确因子对。

还需要在各种常见条件下进行内存可用性测试,这本身就是一个重大的研究项目。

选择的特定N值必须为零,以处理实际最坏情况,因此可以选择一定比例的典型内容、节点结构和运行时条件。例如,可以使用某种形式的随机原地(在正常环境条件下)研究样本,并找到满足95%这些案例的N。

也许可以通过上述方式测试一组案例并将结果放入表格中。这将直接回答你的问题。 我猜想,需要一位受过良好教育的移动软件工程师,尤其是数学和统计方面的能力,全职工作五周才能得出合理的结果。 更实用的估算方法是,可以猜测最坏情况。通过几天的全面研究和几个概念验证应用程序,可以进一步完善此提案。如果没有时间去做,以下是一个很好的初步估计。 考虑一部允许DOM使用1 G字节的手机,因为正常运行条件下,上述目的中使用了3 G字节中的4个G字节。为了得到一个大致数字,可以假设平均节点的内存消耗如下: - 每个节点的40个字符的文本内容每个字符占用2个字节 - 每个节点的4个属性值,每个值有10个字符,每个字符占用2个字节 - 每个节点的4个属性名称,每个名称有4个字符,每个字符占用1个字节 - 在效率较低的情况下,C/C++节点开销为160字节
在这种情况下,最坏情况下的最大节点数为Nworst_case
= 1,024 X 1,024 X 1,024
  / (2 X 40  +  2 X 4 X 10  +  1 X 4 X 4  +  160)

= 3,195,660 . 190,476.

我不会在浏览器中构建一个有三百万个DOM节点的文档,除非完全没有其他选择。请考虑采用下面更常见的做法。
常用做法:
最好的解决方法是远低于N worst_case 可能的限制,并尽可能使用标准HTTP设计技术来降低节点总数。
- 减少每个页面上显示的大小和复杂性,这也提高了视觉和概念上的清晰度。 - 从服务器请求最小量的数据,使用分窗技术推迟尚未可见的内容,或以精心规划的方式在响应时间和内存消耗之间取得平衡。 - 使用异步调用来帮助实现上述的简约。

2
你的160字节C++开销似乎是一个错误的估计。上面的数字表明,每个在C++中使用的元素大约需要10-30kb。我是一名忙碌的有经验的C++程序员,对DOM节点的复杂性感到担忧,但即使是我也会低估它50%。 - Lothar

6

对于那些想知道的人:谷歌有它的DOM大小建议

DOM大小建议


"

一个最佳的DOM树应该:

  • 总共少于1500个节点。
  • 深度不超过32个节点。
  • 没有拥有超过60个子节点的父节点。

一般来说,寻找在需要时仅创建DOM节点,并在不再需要时销毁它们的方法。

如果您的服务器提供了一个大型的DOM树,请尝试加载页面,并手动记录哪些节点被显示出来。也许您可以从加载的文档中删除未显示的节点,并且只能在用户手势(如滚动或按钮单击)之后创建它们。

如果您在运行时创建DOM节点,则Subtree Modification DOM Change Breakpoints可以帮助您确定节点何时被创建。

如果无法避免使用大型DOM树,则改善渲染性能的另一种方法是简化CSS选择器。请参见减少样式计算的范围和复杂性。

"


1

DOM元素过多的情况有很多种。这里是一个React + d3组件,我一直在使用它来渲染许多元素,并更加真实地感受到DOM的限制:

export const App = React.memo((props) => {
  const gridRef = React.useRef(null);
  React.useEffect(() => {
    if (gridRef.current) {
      const table = select(gridRef.current);
      table
        .selectAll("div")
        .data([...new Array(10000)])
        .enter()
        .append("div")
        .text(() => "testing");
    }
    if (props.onElementRendered) {
      props.onElementRendered();
    }
  }, []);
  return <div ref={gridRef} />;
});

在一台配备16GB内存的2021款Macbook Pro上,使用Chrome浏览器时,我发现在大约30,000个元素处开始出现严重的延迟(我认为是绘制步骤)。


1
只是想再添加一些数据。我加载了声称为2064K字节的单页GNU Bison手册。在控制台中,我输入了document.querySelectorAll('*'),答案是22183个节点,这超出了谷歌所谓的“最佳尺寸”。
我没有发现页面加载时有任何延迟(50Mb以太网连接)。加载完成后,点击内部链接、滚动等都没有任何延迟。
这是在我相对强大的台式机上进行的。我在我的Galaxy Note 4上尝试了同样的事情(古老的wifi连接,肯定不是50Mb)。这次(不出所料),我需要等待几秒钟(小于5秒)才能加载。之后,点击链接和滚动又像我眼睛能看到的那样即刻完成。
我不怀疑30,000个React节点可能会带来麻烦,也不怀疑我可以拥有远远多于这个数量的无框架简单HTML节点而毫不费力。我认为需要担心超过1500个节点的想法听起来非常疯狂,但我相信你们的情况可能会有所不同。

感谢您在桌面和移动设备上进行真实世界的测试,并发布于2014年之后。 - Ben Racicot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接