43

我对 JS 的性能有疑问。

说,我有下一个代码:

var divContainer = document.createElement("div"); divContainer.id="container";
var divHeader = document.createElement("div"); divHeader.id="header";
var divData = document.createElement("div"); divData.id="data";
var divFooter = document.createElement("div"); divFooter.id="footer";
divContainer.appendChild( divHeader );
divContainer.appendChild( divData );
divContainer.appendChild( divFooter );
document.getElementById("someElement").appendChild( divContainer );

这段代码只是为其他一些函数创建了 shell 来创建一个网格,创建网格的过程非常复杂并且有很多验证,目前我使用 2 种方法来填充网格,一种在数组变量中创建整个 html另一个创建元素并将它们附加到documentFragment.

我的问题是,据我了解,使用片段时是否真的有性能改进——它们管理内存中的元素,因此它们不会附加到文档,因此不会触发 DOM 重新计算和其他讨厌的东西。但是我创建变量的方式是,在我将容器附加到实际页面之前,它们不会附加到任何 DOM 元素。

所以我想知道前面的代码是否比使用像这样包装它的文档片段具有更好的性能:

var fragment = document.createDocumentFragment();
var divContainer = document.createElement("div"); divContainer.id="container";
var divHeader = document.createElement("div"); divHeader.id="header";
var divData = document.createElement("div"); divData.id="data";
var divFooter = document.createElement("div"); divFooter.id="footer";
divContainer.appendChild( divHeader );
divContainer.appendChild( divData );
divContainer.appendChild( divFooter );
fragment.appendChild( divContainer )
document.getElementById("someElement").appendChild( fragment.cloneNode(true) );

正如我已经说过的,这是一个关于性能的问题,我知道作为最佳实践建议使用片段,但我不能忘记这样做只会在内存中创建一个新对象并且什么都不做,所以我认为在这种情况下放弃片段是有效的。

希望一些 js 大师/上帝会在这里照亮希望并帮助我们解决这个问题。


编辑:所以,我一直在寻找与这个问题相关的东西,似乎 documentFragments 并不一定意味着更好的性能。

它只是节点的“内存中”容器。片段和 a 之间的区别在于<div>片段没有父级,它永远不会在 DOM 上,只是在内存中,这意味着对片段进行的操作更快,因为没有对 DOM 的操作。

W3C关于 documentFragments 的文档非常含糊,但重点是,每个人最喜欢的浏览器都没有使用真正的片段,而是根据这个 MSDN 文档创建一个新文档。这意味着,IE 上的片段速度较慢。

所以,问题是,如果我在变量中创建一个元素<div>例如 a)但不将它附加到 DOM,添加元素(div、表等)和东西,然后在所有工作完成之后(循环、验证,元素样式),该元素被附加,它与片段相同吗?

鉴于 IE 使用“假”片段这一事实,我至少在 IE 中使用这种方法(使用诸如 div 之类的元素,而不是片段)会更好,我真的不在乎 IE,但我需要测试它(办公室的政策)。

另外,如果我像这样在数组上创建所有 html:

var arrHTML = ["<table>","<tr>", ....]; 

然后这样做

document.getElementById("someElement").innerHTML = arrHTML.join(""); 

它在 IE 上要快得多,但其他主要浏览器(FF、Chrome、Safari 和 Opera)在使用容器然后附加它(片段或 div)时表现更好。

所有这一切都是因为创建所有元素的过程非常快,大约 8 - 10 秒创建多达 24 列的 20,000 行,这是很多元素/标签,但是浏览器似乎冻结了几秒钟它们都是一次附加的,如果我尝试一个接一个地附加它们,那就太糟糕了。

再次感谢大家,这真的很有趣。

4

7 回答 7

35

Document Fragment is much faster when it is used to insert a set of elements in multiple places. Most answers here point out its un-utility, but this is to demonstrate its strength.

Lets take an example.

Say we need to append 20 divs in 10 elements with class container.

Without:

var elements = [];
for(var i=20; i--;) elements.push(document.createElement("div"));

var e = document.getElementsByClassName("container");
for(var i=e.length; i--;) {
  for(var j=20; j--;) e[i].appendChild(elements[j].cloneNode(true));
}


With:

var frag = document.createDocumentFragment();
for(var i=20; i--;) frag.appendChild(document.createElement("div"));

var e = document.getElementsByClassName("container");
for(var i=e.length; i--;) e[i].appendChild(frag.cloneNode(true));

For me using a document fragment turns out to be 16 times faster on Chrome 48.

Test on JsPerf

于 2016-02-06T15:16:21.587 回答
11

通常,您希望使用片段来避免重排(重新绘制页面)。一个很好的情况是,如果您循环某些内容并在循环中附加,但是,我认为现代浏览器已经对此进行了优化。

我在这里设置了一个 jsPerf 来说明何时使用片段的一个很好的例子。您会注意到在 Chrome 中几乎没有区别(我认为现代优化在工作中),但是,在 IE7 中,我得到 0.08 ops/sec 没有片段,3.28 ops/sec 有片段。

因此,如果您要遍历一个大型数据集并附加很多元素,请改用片段,这样您就只有一个回流。如果您只附加到 dom 几次,或者您只针对现代浏览器,则没有必要。

于 2013-01-07T21:00:24.747 回答
6

我写了一个jspref来测试它,节点片段似乎快了2.34%

http://jsperf.com/document-fragment-test-peluchetti

于 2013-01-07T20:11:31.837 回答
0

以我的经验,dom 操作通常仅在调用堆栈为空后发生。如果我将很多 dom 操作放在循环中,浏览器会冻结一段时间,然后立即显示所有内容。如果需要,您可以使用 setTimeout 更频繁地显示结果来打破堆栈。出于这个原因,我相信这两种方法的表现应该相似。有时这实际上很奇怪,因为如果在一个堆栈中更改了某个元素,则在更改之前您将永远看不到它的状态(进度通知对象有这个问题,在循环期间从未更新过 innerHTML - 只是开始状态,然后是最终状态)。

于 2013-12-25T13:21:52.497 回答
0

<!DOCTYPE html>
<html>
<head>
    <title>TODO supply a title</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

</head>
<body>
<div ms-controller='for1'>
    <ul>

    </ul>
</div>

<script>
    var ul = document.querySelector('ul');
    console.time('no fragment');
    for(var i=0;i<1000000;i++){
        var li = document.createElement('li');
        li.innerText = i;

        ul.appendChild(li);
    }
    console.timeEnd('no fragment');

    console.time('has fragment');;
    var frg = document.createDocumentFragment()
    for(var i=0;i<1000000;i++){
        var li = document.createElement('li');
        li.innerText = i+'fragment';
        frg.appendChild(li);

    }
    ul.appendChild(frg)
    console.timeEnd('has fragment');
</script>
</body>
</html>

结果是没有片段:1615.278ms testFragment.html:36 有片段:2908.286ms

所以,没有 Fragment 更快。我认为原因是铬做了一些事情。

于 2016-08-18T07:50:04.303 回答
0

我有与 OP 完全相同的问题,在阅读了所有答案和评论后,似乎没有人真正理解 OP 的要求。

我从 Nicola Peluchetti 发布的测试中得到启发,并对其进行了一些修改。

片段测试不是将元素附加到 a<div>然后附加到,而是将元素直接附加到它 (the ) 而不是首先附加到. 此外,为了避免任何隐藏的开销成本,两个测试都从创建容器和 .开始,而每个测试只使用一个或另一个。documentFragmentdocumentFragment<div><div>documentFragment

我认为最初的问题是,基本上,使用 a或 a作为容器执行单个节点附加是否更快?<div>documentFragment

看起来使用 a<div>更快,至少在 Chrome 49 上。

http://jsperf.com/document-fragment-test-peluchetti/39

我能想到的唯一用例documentFragment(目前)是它是否需要更少的内存(这可能可以忽略不计),或者如果你有一堆你不想放入“容器”的兄弟节点要附加“ 元素。documentFragment就像一个包装器,它会溶解,只留下它的内容。

于 2016-03-16T04:50:55.370 回答
0

来自 wolfram77 的 jsperf 在非片段示例中包含一个额外的 for 循环,这是导致性能差异的主要原因,而不是 DocumentFragment。通过删除这个额外的 for 循环,您可以获得相同的结果,但性能完全不同:

jsperf.com 上的示例

所以我仍然看不到脚本部分的性能优势,但是当它必须为每个附加元素重新绘制时,浏览器中可能会有一个。

于 2017-12-11T23:15:09.413 回答