Подтвердить что ты не робот

Действительно ли использование фрагмента документа действительно повышает производительность?

У меня есть сомнения относительно производительности в 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 метода для заполнения сетки, один из которых создает весь 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 медленнее.

Итак, вопрос превалирует, , если я создаю элемент (a <div>, например) в переменной, но НЕ ПРИНИМАЙТЕ В DOM, добавьте элементы (divs, tables и т.д.) и т.д. и после выполнения всей работы (циклы, проверки, стилизация элементов), этот элемент добавлен, то же самое, что и фрагмент?

Учитывая тот факт, что IE использует "фальшивый" фрагмент, я бы сказал, по крайней мере, в IE, используя этот подход (используя такой элемент, как div, а не фрагмент), лучше, я действительно не забочусь о IE, но Мне нужно проверить его (офисная политика).

Кроме того, если я создам все html в массиве, например:

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

а затем сделайте это

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

Он быстрее работает в IE, но другие основные браузеры (FF, Chrome, Safari и Opera) работают лучше при использовании контейнера, а затем добавляют его (фрагмент или div).

Все это связано с тем, что процесс создания всех элементов выполняется очень быстро, около 8-10 секунд, чтобы создать до 20 000 строк с 24 столбцами, много элементов/тегов, но браузер, похоже, несколько секунд, когда все они добавлены сразу, если я попытаюсь добавить их один за другим, это ад.

Спасибо, ребята, это действительно интересно и весело.

4b9b3361

Ответ 1

Фрагмент документа намного быстрее, когда он используется для вставки набора элементов в нескольких местах. Большинство ответов здесь указывают на его не-полезность, но это демонстрирует свою силу.

Давайте возьмем пример.

Скажем, нам нужно добавить 20 div в 10 элементов с контейнером класса.

Без:

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


С:

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 в 16 раз быстрее.

Тест на JsPerf

Ответ 2

Обычно вы хотите использовать фрагмент, чтобы избежать перекомпоновки (перерисовка страницы). Хорошим случаем было бы, если бы вы зацикливались на чем-то и добавляли в цикле, однако, я думаю, что современные браузеры оптимизируются для этого уже.

Я установил jsPerf, чтобы проиллюстрировать хороший пример того, когда использовать фрагмент здесь. В Chrome вы заметите, что вряд ли есть разница (современная оптимизация на работе, я полагаю), однако в IE7 я получаю .08 ops/sec без фрагмента, 3.28 ops/sec с фрагментом.

Итак, если вы зацикливаетесь на большой набор данных и добавляете много элементов, используйте фрагмент вместо этого, поэтому у вас есть только один пересчет. Если вы просто добавляете в дом несколько раз или используете только современные браузеры, это необязательно.

Ответ 4

В моем опыте операции dom обычно происходят только после того, как стек вызовов пуст. Если я помещу много операций dom в цикл, браузер просто зависает в течение некоторого времени, а затем отображает все сразу. Вы можете разбить стек, используя setTimeout, чтобы отображать результат чаще, если хотите. По этой причине я считаю, что оба метода должны выполняться аналогичным образом. Иногда это бывает очень странно, потому что если в одном стеке вы меняете какой-то элемент, вы никогда не увидите его состояние до изменения (возникла эта проблема с объектом уведомления о прогрессе, который innerHTML никогда не обновлялся во время цикла - только начальный статус, а затем окончательный).

Ответ 5

У меня был тот же самый вопрос, что и OP, и, прочитав все ответы и комментарии, казалось, что никто не понимал, что спрашивал OP.

Я взял реплику из теста Nicola Peluchetti и немного изменил ее.

Вместо добавления элементов к <div> и , а затем, добавляя к documentFragment, тест фрагмента получает непосредственно прикрепленные к нему элементы (documentFragment) вместо первого в <div>. Кроме того, чтобы избежать любых скрытых накладных расходов, оба теста начинаются с создания как контейнера <div>, так и documentFragment, в то время как каждый тест использует только тот или иной.

Я взял исходный вопрос, в основном, быстрее ли сделать один добавочный узел с использованием <div> или documentFragment в качестве контейнера?

Похоже, что использование <div> выполняется быстрее, по крайней мере, на Chrome 49.

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

Единственный случай использования, который я могу придумать для documentFragment (на данный момент), - это то, что он занимает меньше памяти (что может быть незначительным), или если у вас есть куча родственных узлов для добавления, которые вы не хотите для ввода в элемент "контейнер". documentFragment похож на обертку, которая растворяется, оставляя только ее содержимое.

Ответ 6

<!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>

Ответ 7

jsperf из wolfram77 содержит дополнительный цикл for в примере без фрагмента, который является основной причиной разницы в производительности, а не DocumentFragment. Удалив этот дополнительный цикл, вы можете добиться того же результата, но производительность совершенно другая:

Пример на jsperf.com

Таким образом, я все еще не вижу преимущества производительности для части сценариев, но может быть один в браузере, когда он должен перерисовываться для каждого добавленного элемента.