web-dev-qa-db-ja.com

ドキュメントフラグメントを使用すると、本当にパフォーマンスが向上しますか?

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 );

このコードは、グリッドを作成する他のいくつかの関数のシェルを作成するだけです。グリッドを作成するプロセスは非常に複雑で、多くの検証が行われています。現在、グリッドを埋めるために2つの方法を使用しています。もう1つは要素を作成して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 guru/godがここで希望の光を輝かせて、この問題で私たちを助けてくれるでしょう。


Edit:したがって、私はこの問題に関連するものを探していましたが、documentFragmentsが必ずしもパフォーマンスの向上を意味するわけではないようです。

ノードの「メモリ内」コンテナにすぎません。フラグメントと言う違いは、<div>は、フラグメントが親を持たず、メモリ上だけでDOM上に存在することはないことを意味します。これは、DOMの操作がないため、フラグメント上で行われる操作が高速であることを意味します。

W3C のdocumentFragmentsに関するドキュメントは非常にあいまいですが、要点として、また、誰もがお気に入りのブラウザは実際のフラグメントを使用せず、代わりに this MSDN documentation に従って新しいドキュメントを作成します。つまり、IE=のフラグメントは低速です。

したがって、質問が優先されます、要素を作成すると(a <div>例)変数ではあるが、DOMに追加しないで、要素を追加する(div、テーブルなど)そしてスタッフとすべての作業が完了した後(ループ、検証、要素のスタイリング)、その要素が追加され、それは同じです断片として?

IEが「偽の」フラグメントを使用しているという事実を考えると、少なくともIEでそのアプローチを使用します(divなどの要素を使用して、フラグメント)の方が良いですが、IE=は気にしませんが、テストする必要があります(オフィスのポリシー))。

また、配列にすべてのhtmlを次のように作成した場合:

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

そしてこれを行う

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

IEの方がはるかに高速ですが、他の主要なブラウザー(FF、Chrome、Safari、およびOpera))は、コンテナーを使用してから追加すると(フラグメントまたはdiv)、パフォーマンスが向上します。

これはすべて、すべての要素を作成するプロセスが非常に高速に行われるためです。24列で最大20,000行を作成するのに約8〜10秒かかります。これは多くの要素/タグですが、ブラウザーが数秒フリーズするようです。それらはすべて一度に追加されます。1つずつ追加しようとすると、それは地獄です。

皆さん、ありがとうございました。これは本当に面白くて楽しいです。

37
Sam Ccp

ドキュメントフラグメントは、セットを挿入するために使用すると、はるかに高速です複数の場所内の要素の数。ここでのほとんどの回答はその実用性を指摘していますが、これはその強さを示すためです。

例を見てみましょう。

20 divs10要素にクラスcontainerを追加する必要があるとしましょう。

なし:

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));

私にとってドキュメントフラグメントを使用すると、Chrome 48。

JsPerfでテスト

28
wolfram77

通常は、リフロー(ページの再描画)を回避するためにフラグメントを使用します。良い例は、何かをループしてループ内に追加した場合ですが、最近のブラウザはすでにこれを最適化していると思います。

JsPerfを設定して、フラグメントを使用する適切な例を示します here 。 Chromeでほとんど違いがないことに気づくでしょう(現在の最適化は作業中であると思います)、しかし、IE7ではフラグメントなしで.08 ops/sec、3.28 ops/secを取得します。フラグメント付き。

したがって、大きなデータセットをループして要素をたくさん追加する場合は、代わりにフラグメントを使用して、リフローを1つだけにします。 DOMに数回追加するだけの場合、または最新のブラウザのみをターゲットにする場合は、必要ありません。

10
Snuffleupagus

これをテストするためにjsprefを作成しましたが、ノードフラグメントが2.34%高速であるようです

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

6

私の経験では、dom操作は通常、コールスタックが空になった後にのみ発生します。ループに多くのdom操作を入れると、ブラウザはしばらくフリーズし、すべてを一度に表示します。必要に応じてsetTimeoutを使用して結果をより頻繁に表示することにより、スタックを解除できます。このため、どちらの方法も同じように機能するはずです。これは実際には非常に奇妙なことです。なぜなら、1つのスタックで一部の要素を変更すると、変更前の状態が表示されないためです(ループ中にinnerHTMLが更新されなかった進行通知オブジェクトにこの問題がありました-ステータスを開始し、最後に)。

1
Miro Krajci
<!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 has fragment:2908.286ms

したがって、フラグメントはより高速です。その理由は、chromeが何らかのことをしたためだと思います。

0
Jack Dawson

Wolfram77のjsperfには、DocumentFragmentではなく、パフォーマンスの違いの主な原因である非フラグメントの例に追加のforループが含まれています。この追加のforループを削除すると、同じ結果を得ることができますが、パフォーマンスは完全に異なります。

jsperf.comの例

そのため、スクリプト部分のパフォーマンス上の利点はまだわかりませんが、追加された要素ごとに再描画する必要がある場合、ブラウザーに1つある可能性があります。

0
benandunt

私はOPとまったく同じ質問があり、すべての回答とコメントを読んだ後、誰もがOPが求めていることを本当に理解しているようには見えませんでした。

Nicola Peluchettiが投稿したテストからヒントを得て、少し修正しました。

_<div>_およびthenに要素を追加する代わりに、documentFragmentに追加するフラグメントテストでは、代わりに要素に直接追加された要素(documentFragment)を取得します最初に_<div>_に。また、隠れたオーバーヘッドコストを回避するために、両方のテストは_<div>_コンテナーとdocumentFragmentの両方を作成することから始まりますが、各テストはどちらか一方のみを使用します。

私は元の質問を基本的に、より高速ですか_<div>_またはdocumentFragmentをコンテナとして使用してノードの[単一の追加を実行する方が速いですか?

_<div>_を使用すると、少なくともChrome 49。

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

(現時点で)documentFragmentについて考えられる唯一のユースケースは、メモリが少ない(無視できる可能性がある)場合、または追加したくない多数の兄弟ノードがある場合です。 「コンテナ」要素に配置します。 documentFragmentは、内容を残して解散するラッパーのようなものです。

0
papiro