【问题标题】:Why is appending to an element not yet in the DOM faster than using a javascript Fragment?为什么附加到尚未在 DOM 中的元素比使用 javascript Fragment 更快?
【发布时间】:2025-12-10 18:55:02
【问题描述】:

考虑将lis 附加到ul 的这三个版本:

朴素版(慢 20%):

var ul = document.getElementById('targetUl');

for (var i = 0; i < 200; i++) {
  var li = document.createElement('li');
  li.innerHTML = Math.random();
  ul.appendChild(li);
}

使用 JavaScript 片段(慢 4%):

var ul = document.getElementById('targetUl'),
    fragment = document.createDocumentFragment();

for (var i = 0; i < 200; i++) {
  var li = document.createElement('li');
  li.innerHTML = Math.random();
  fragment.appendChild(li);
}
ul.appendChild(fragment);

追加到尚未在 DOM 中的元素(快 1.26%):

var ul = document.createElement('ul'),
    div = document.getElementById('targetDiv');

for (var i = 0; i < 200; i++) {
  var li = document.createElement('li');
  li.innerHTML = Math.random();
  ul.appendChild(li);
}
div.appendChild(ul);

为什么追加到内存中的 DOM 元素比追加到 Fragment 更快?由于fragment 是为此唯一目的而创建的,它不应该更快吗?除了不必在追加之前包含*元素之外,使用fragment 对内存中保存的元素有什么好处吗?

检查来自 jsperf 的测试输出:http://jsperf.com/javascript-fragments-tests

【问题讨论】:

  • 不参考底层代码或设计,你得到的任何答案都是猜测。我的观点是,片段是任何 DOM 元素的通用容器,而 UL 非常具体,只能有 LI 子元素,所以当元素被附加时片段必须做的工作,而 UL 没有。我认为 ±4% 不显着。
  • 不要太担心微优化
  • 怎样才能让 Fragment 更快? (假设性地询问在 Fragment 与常规 DOM 元素中可以进行哪些优化)
  • @JKillian 它应该是为这个精确操作设计的最小文档对象。我不确定他们可以使用哪些优化来实现这一点。

标签: javascript


【解决方案1】:

从文档片段中插入多个子代(特别是当您的测试有 200 个子代时)比插入单个父代&lt;ul&gt; 标记要多一些工作。

因此,对于片段,您将片段中的 200 个 &lt;li&gt; 元素重新设置为 DOM 中的 &lt;ul&gt; 标记。

使用您的最后一个代码块,您只是通过将 &lt;ul&gt; 标记插入 DOM 来重新设置它的父级。

因此,在您的特定示例中,使用文档片段为插入 DOM 创造了比您运行上一个示例的方式更多的工作,后者只需插入单个 &lt;ul&gt; 标记。


当您想要跟踪同一级别的一大堆元素然后使用一行代码插入它们并且您的父级已经在 DOM 中时,片段具有其战术优势。但是,与您可以在实际父节点下的同一级别从 DOM 中收集所有项目然后仅插入该父节点的场景相比,它并不总是最快的方式。

【讨论】:

  • 那么为什么有人想要使用文档片段而不是仅仅创建您计划附加到文档的任何元素树的父节点?与仅仅创建父元素相比,是什么让文档片段如此特别?
  • @chiliNUT - 有时父节点已经在 DOM 中,您想收集 DOM 之外的子节点,然后一次将它们全部插入。如果是这种情况,那么使用片段的代码更少,因为不使用片段意味着您必须跟踪子节点数组或创建自己的虚拟父节点,然后您必须将所有子节点单独插入 DOM .使用片段,您可以使用一行代码将它们全部插入。在某些情况下,这是一种编程便利。
  • 我不确定我是否理解您的回答。第二个测试比第三个测试做更多的工作,因为它必须重新定义单个元素,即使该元素有很多子元素。如果我在片段中的lis 中添加一个*元素,性能是否相同,因为这样它只会重新定义一个具有许多子元素的元素?
  • @jfriend00 啊,有道理。这很有趣。我有一些使用可能使用父元素重写的文档片段的代码。您列出的标准是我将来在处理 dom 节点插入时需要考虑的内容。谢谢!
  • @agconti - 从片段插入的性能将与片段中*项目的数量成正比,因为这是一次必须重新设置父项并插入 DOM 的数量。这些*对象下方的子对象在插入时不会花费任何成本(除了完成重新布局时的布局复杂性),因为它们附加到父对象,因此当父对象移动时,子对象免费使用它(这就是DOM 的分层性质的优势)。
【解决方案2】:

我的猜测是……

对于 Naive 版本。 在 DOM 中添加元素时的循环变得越来越长。 随着 DOM 中存在的 ul 标记中加载的元素越多,处理要附加的后续元素所需的时间就越长。 这需要更长的时间,因为您正在修改 DOM 中已经存在的标签。

对于 Javascript 片段 该片段在 HTML 文档中尚不存在,仅在片段中插入一系列 li 后才附加到 ul 中。 只有在循环部分发生后端处理,然后在 DOM 中插入包含多个 lis 的片段。当然,由于 ul 存在于 DOM 中,因此仍然存在一个循环,因为领养孩子需要一些时间。

用于附加到尚未在 DOM 中的元素。 这个主要在后端工作,并且使 UI 解释器的工作量更少,因为它就像在一堆论文之上添加另一篇论文(一切都在要添加到一堆论文之上的论文上工作)。

我的解释可能不准确,但至少我是在分享一个想法。这一直是我们游戏开发者面临的最大问题之一。

【讨论】:

    最近更新 更多