Éviter les mises en page volumineuses et complexes et le thrashing de mise en page

La mise en page est l'endroit où le navigateur détermine les informations géométriques des éléments (leur taille et leur emplacement sur la page). Chaque élément dispose d'informations de dimensionnement explicites ou implicites en fonction du CSS utilisé, du contenu de l'élément ou d'un élément parent. Ce processus est appelé "mise en page" dans Chrome.

C'est lors de la mise en page que le navigateur détermine les informations géométriques des éléments : leur taille et leur emplacement sur la page. Chaque élément dispose d'informations de dimensionnement explicites ou implicites en fonction du CSS utilisé, du contenu de l'élément ou d'un élément parent. Ce processus est appelé "mise en page" dans Chrome (et les navigateurs dérivés tels qu'Edge) et Safari. Dans Firefox, cela s'appelle Reflow, mais le processus est en fait le même.

Comme pour les calculs de style, les préoccupations immédiates concernant le coût de la mise en page sont les suivantes :

  1. Nombre d'éléments nécessitant une mise en page, qui est un sous-produit de la taille DOM de la page.
  2. La complexité de ces mises en page

Résumé

  • La mise en page a un impact direct sur la latence d'interaction
  • La mise en page est généralement limitée à l'ensemble du document.
  • Le nombre d'éléments DOM affecte les performances. Dans la mesure du possible, évitez de déclencher la mise en page.
  • Évitez les mises en page synchrones forcées et les mises en page inefficaces. Lisez les valeurs de style, puis apportez des modifications de style.

Effets de la mise en page sur la latence d'interaction

Lorsque l'utilisateur interagit avec la page, ces interactions doivent être aussi rapides que possible. Le temps nécessaire pour qu'une interaction se termine (qui se termine lorsque le navigateur présente le frame suivant pour afficher les résultats de l'interaction) est appelé latence d'interaction. C'est un aspect des performances de la page que la métrique Interaction to Next Paint mesure.

Le temps nécessaire au navigateur pour présenter le frame suivant en réponse à une interaction utilisateur est appelé délai de présentation de l'interaction. L'objectif d'une interaction est de fournir un retour visuel afin de signaler à l'utilisateur que quelque chose s'est produit, et les mises à jour visuelles peuvent nécessiter un certain travail de mise en page afin d'atteindre cet objectif.

Pour que l'INP de votre site Web soit aussi faible que possible, il est important d'éviter la mise en page dans la mesure du possible. S'il n'est pas possible d'éviter complètement la mise en page, il est important de limiter ce travail afin que le navigateur puisse présenter le frame suivant rapidement.

Évitez la mise en page autant que possible.

Lorsque vous modifiez des styles, le navigateur vérifie si l'une des modifications nécessite le calcul de la mise en page et pour la mise à jour de l'arborescence de rendu. Les modifications apportées aux "propriétés géométriques", telles que les largeurs, les hauteurs, la gauche ou le haut, doivent toutes être mises en page.

.box {
 
width: 20px;
 
height: 20px;
}

/**
  * Changing width and height
  * triggers layout.
  */


.box--expanded {
 
width: 200px;
 
height: 350px;
}

La mise en page est presque toujours limitée à l'ensemble du document. Si vous avez beaucoup d'éléments, il vous faudra beaucoup de temps pour déterminer leurs emplacements et dimensions.

S'il n'est pas possible d'éviter la mise en page, utilisez à nouveau les outils pour les développeurs Chrome afin de déterminer le temps nécessaire et de déterminer si la mise en page est à l'origine d'un goulot d'étranglement. Tout d'abord, ouvrez les outils de développement, accédez à l'onglet "Chronologie", appuyez sur "Enregistrer" et interagissez avec votre site. Lorsque vous arrêtez l'enregistrement, un récapitulatif des performances de votre site s'affiche :

Les outils de développement affichent un temps long dans la mise en page.

En examinant la trace de l'exemple ci-dessus, nous constatons que plus de 28 millisecondes sont consacrées à la mise en page pour chaque frame, ce qui est beaucoup trop élevé, étant donné que nous disposons de 16 millisecondes pour afficher un frame à l'écran dans une animation. Vous pouvez également voir que DevTools indique la taille de l'arborescence (1 618 éléments dans ce cas) et le nombre de nœuds nécessitant une mise en page (5 dans ce cas).

Gardez à l'esprit que le conseil général est d'éviter la mise en page dans la mesure du possible, mais ce n'est pas toujours possible. Si vous ne pouvez pas éviter la mise en page, sachez que son coût est lié à la taille du DOM. Bien que la relation entre les deux ne soit pas étroitement couplée, les DOM plus volumineux entraînent généralement des coûts de mise en page plus élevés.

Éviter les mises en page synchrones forcées

L'envoi d'un frame à un écran se fait dans l'ordre suivant :

Utilisation de Flexbox comme mise en page.

Le code JavaScript est d'abord exécuté, puis les calculs de style, puis la mise en page. Il est toutefois possible de forcer un navigateur à effectuer une mise en page antérieure avec JavaScript. C'est ce qu'on appelle la mise en page synchrone forcée.

La première chose à retenir est que lorsque le code JavaScript s'exécute, toutes les anciennes valeurs de mise en page du frame précédent sont connues et disponibles pour vous. Par exemple, si vous souhaitez écrire la hauteur d'un élément (appelons-le "boîte") au début du frame, vous pouvez écrire du code comme suit :

// Schedule our function to run at the start of the frame:
requestAnimationFrame
(logBoxHeight);

function logBoxHeight () {
 
// Gets the height of the box in pixels and logs it out:
  console
.log(box.offsetHeight);
}

Les choses se compliquent si vous avez modifié les styles de la zone avant de demander sa hauteur :

function logBoxHeight () {
  box
.classList.add('super-big');

 
// Gets the height of the box in pixels and logs it out:
  console
.log(box.offsetHeight);
}

Pour répondre à la question sur la hauteur, le navigateur doit d'abord appliquer le changement de style (en raison de l'ajout de la classe super-big), puis ensuite exécuter la mise en page. Il ne pourra alors renvoyer la hauteur correcte. Il s'agit d'un travail inutile et potentiellement coûteux.

Pour cette raison, vous devez toujours regrouper vos lectures de style et les effectuer en premier (où le navigateur peut utiliser les valeurs de mise en page du frame précédent), puis effectuer les écritures:

Si elle est correctement réalisée, la fonction ci-dessus se présente comme suit :

function logBoxHeight () {
 
// Gets the height of the box in pixels and logs it out:
  console
.log(box.offsetHeight);

  box
.classList.add('super-big');
}

Dans la plupart des cas, vous n'avez pas besoin d'appliquer des styles, puis d'interroger des valeurs. L'utilisation des valeurs du dernier frame devrait suffire. Exécuter les calculs de style et la mise en page de manière synchrone et plus tôt que le navigateur le souhaite peut entraîner des goulots d'étranglement. Il est donc préférable de ne pas le faire.

Éviter le thrashing de la mise en page

Il existe un moyen de rendre les mises en page synchrones forcées encore pires : en en effectuant beaucoup de suite. Examinez ce code :

function resizeAllParagraphsToMatchBlockWidth () {
 
// Puts the browser into a read-write-read-write cycle.
 
for (let i = 0; i < paragraphs.length; i++) {
    paragraphs
[i].style.width = `${box.offsetWidth}px`;
 
}
}

Ce code effectue une boucle sur un groupe de paragraphes et définit la largeur de chaque paragraphe pour qu'elle corresponde à celle d'un élément appelé "box". Cela semble assez inoffensif, mais le problème est que chaque itération de la boucle lit une valeur de style (box.offsetWidth), puis l'utilise immédiatement pour mettre à jour la largeur d'un paragraphe (paragraphs[i].style.width). Lors de la prochaine itération de la boucle, le navigateur doit tenir compte du fait que les styles ont changé depuis la dernière requête de offsetWidth (lors de l'itération précédente). Il doit donc appliquer les modifications de style et exécuter la mise en page. Cela se produit à chaque itération.

Pour résoudre ce problème, vous devez à nouveau lire, puis écrire des valeurs :

// Read.
const width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth () {
 
for (let i = 0; i < paragraphs.length; i++) {
   
// Now write.
    paragraphs
[i].style.width = `${width}px`;
 
}
}

Si vous souhaitez garantir la sécurité, envisagez d'utiliser FastDOM, qui regroupe automatiquement vos lectures et écritures pour vous, et qui vous évite de déclencher des mises en page synchrones forcées ou du thrashing de mise en page accidentellement.