web-dev-qa-db-fra.com

L'utilisation d'un fragment de document améliore-t-elle vraiment les performances?

J'ai un doute sur les performances en JS.

Dis, j'ai le code suivant:

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

Ce code crée simplement le Shell pour quelques autres fonctions afin de créer une grille. Le processus de création de la grille est très complexe et comporte de nombreuses validations. J'utilise actuellement deux méthodes pour remplir la grille. L'une crée le code HTML complet dans une variable de tableau. et l’autre créant des éléments et les ajoutant à une documentFragment.

Ma question est de savoir s'il y a vraiment une amélioration des performances lors de l'utilisation de fragments, tels que je les comprends: ils gèrent les éléments en mémoire, de sorte qu'ils ne sont pas attachés au document, évitant ainsi le recalcul des DOM et autres problèmes. Mais de la manière dont je crée mes variables, elles ne sont attachées à aucun élément DOM tant que je n’ajoute pas le conteneur à la page réelle.

Je me demandais donc si le code précédent présentait de meilleures performances que l’utilisation d’un fragment de document qui l’encapsule de la manière suivante:

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

Comme je l’ai déjà dit, c’est une question de performance, je sais qu’il est recommandé d’utiliser des fragments, mais je ne peux oublier de penser que cela ne fait que créer un nouvel objet en mémoire et ne fait rien, alors je suppose que le fait de laisser tomber le fragment dans ce cas est valide.

J'espère qu'un guru/dieu js apportera une lumière d'espoir et nous aidera à résoudre ce problème.


Edit: Je cherche donc des informations sur ce problème et il semble que documentFragments ne signifie pas nécessairement une meilleure performance.

C'est juste un conteneur de noeuds "en mémoire". La différence entre un fragment et disons, un <div> est que le fragment n'a pas de parent et qu'il ne sera jamais dans le DOM, juste en mémoire, ce qui signifie que les opérations effectuées sur le fragment sont plus rapides puisqu'il n'y a aucune manipulation du DOM.

La documentation de W3C sur documentFragments est très vague, mais le navigateur préféré de tout le monde n’utilise pas de fragments réels, mais crée un nouveau document conformément à cette documentation MSDN . Ce qui signifie que les fragments sur IE sont plus lents.

Donc, la question prévaut, si je crée un élément (un <div> par exemple) dans une variable mais ne l'appliquez pas au DOM, ajoutez des éléments (divs, tables, etc.) et tout ça et après que tout le travail soit fait (boucles, validations, style des éléments), cet élément est ajouté, est-ce la même chose qu'un fragment?

Étant donné que IE utilise un "faux" fragment, je dirais qu'au moins dans IE, cette approche (utiliser un élément tel que div, pas un fragment) est préférable, je ne le fais vraiment pas. ne me soucie pas de IE mais je dois le tester (politique du bureau). 

Aussi, si je crée tout le code HTML sur un tableau comme ceci: 

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

et ensuite faire ceci 

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

Il est bien plus rapide sous IE, mais d'autres navigateurs majeurs (FF, Chrome, Safari et Opera) fonctionnent mieux lors de l'utilisation d'un conteneur, puis de son ajout (fragment ou div).

Tout cela est dû au fait que le processus de création de tous les éléments est très rapide: environ 8 à 10 secondes pour créer jusqu'à 20 000 lignes avec 24 colonnes, cela fait beaucoup d'éléments/balises, mais le navigateur semble se figer quelques secondes lorsque ils sont tous ajoutés en même temps, si j'essaie de les ajouter un à un, c'est l'enfer.

Merci encore, c'est vraiment intéressant et amusant.

34
Sam Ccp

Fragment de document} est beaucoup plus rapide lorsqu'il est utilisé pour insérer un ensemble d'éléments dans plusieurs endroits. La plupart des réponses ici soulignent sa non-utilité, mais c'est pour démontrer sa force.

Prenons un exemple.

Supposons que nous ayons besoin d'ajouter 20 divs à 10 éléments avec la classe conteneur.

Sans pour autant:

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


Avec:

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

Pour moi, utiliser un fragment de document s’avère 16 fois plus rapide sur Chrome 48.

Test sur JsPerf

21
wolfram77

Normalement, vous voudriez utiliser un fragment pour éviter les reflux (repeindre la page). Un bon exemple serait si vous faisiez une boucle sur quelque chose et que vous y ajoutiez une boucle, cependant, je pense que les navigateurs modernes optimisent déjà cela.

J'ai mis en place un jsPerf pour illustrer un bon exemple d'utilisation d'un fragment ici . Vous remarquerez dans Chrome qu’il n’ya guère de différence (l’optimisation moderne au travail, je suppose), cependant, dans IE7, je reçois 0,08 ops/s sans le fragment, 3,28 ops/s avec un fragment.

Donc, si vous passez en boucle sur un ensemble de données volumineux et que vous ajoutez BEAUCOUP d’éléments, utilisez plutôt un fragment, de sorte que vous n’avez qu’une redondance. Si vous ne rajoutez que quelques fois le nom de domaine ou si vous ne ciblez que les navigateurs modernes, ce n'est pas nécessaire.

10
Snuffleupagus

J'ai écrit un jspref pour tester cela et il apparaît que le fragment de nœud est 2,34% plus rapide

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

6
Nicola Peluchetti

D'après mon expérience, les opérations dom ne se produisent généralement que lorsque la pile d'appels est vide. Si je mets beaucoup d'opérations dom dans la boucle, le navigateur se bloque pendant un certain temps, puis affiche tout en même temps. Vous pouvez casser la pile en utilisant setTimeout pour afficher le résultat plus souvent si vous le souhaitez. Pour cette raison, je pense que les deux méthodes devraient fonctionner de la même manière. C’est en fait parfois très étrange, car si vous modifiez un élément dans une pile, vous ne verrez jamais son état avant le changement (problème de notification de progression qui n’avait jamais été mis à jour par innerHTML au cours de la boucle).

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>

le résultat estno fragment: 1615.278ms testFragment.html: 36 a fragment: 2908.286ms

donc, aucun fragment n’est plus rapide… .. Je pense que la raison en est que le chrome a fait quelque chose.

0
Jack Dawson

Le jsperf de wolfram77 contient une boucle for supplémentaire dans l'exemple non-fragment qui est la cause principale de la différence de performances, pas le DocumentFragment. En supprimant cette boucle for supplémentaire, vous pouvez obtenir le même résultat, mais les performances sont complètement différentes:

Exemple sur jsperf.com

Donc, je ne vois toujours pas d’avantage en termes de performances sur la partie script mais il se peut qu’il en existe un dans le navigateur quand il doit repeindre pour chaque élément ajouté.

0
benandunt

J'avais exactement la même question que le PO, et après avoir lu toutes les réponses et les commentaires, il ne semblait pas que quiconque ait vraiment compris ce que le PO demandait. 

J'ai pris note du test publié par Nicola Peluchetti et l'ai modifié un peu.

Au lieu d'ajouter des éléments à <div> et then à la documentFragment, le test de fragment obtient les éléments qui lui sont ajoutés directement (la documentFragment) au lieu de la première à la <div>. De plus, pour éviter les frais généraux cachés, les deux tests commencent par créer à la fois le conteneur <div> et la documentFragment, chaque test utilisant uniquement l'un ou l'autre.

Je pensais que la question initiale était, en gros, est-il plus rapide de faire un seul append de nœuds en utilisant un <div> ou un documentFragment comme conteneur?

On dirait que l'utilisation d'un <div> est plus rapide, du moins sur Chrome 49. 

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

Le seul cas d'utilisation auquel je peux penser pour documentFragment (pour le moment) est si cela prend moins de mémoire (ce qui pourrait être négligeable), ou si vous avez un groupe de nœuds frères à ajouter que vous ne souhaitez pas mettre en " "conteneur". La documentFragment est comme une enveloppe qui se dissout en ne laissant que son contenu.

0
papiro