Corriger l'instabilité de la mise en page

Découvrez comment utiliser WebPageTest pour identifier et résoudre les problèmes d'instabilité de la mise en page.

Dans un précédent article, j'ai expliqué comment mesurer le Cumulative Layout Shift (CLS) dans WebPageTest. Le CLS est une agrégation de tous les décalages de mise en page. Dans cet article, j'ai donc pensé qu'il serait intéressant d'examiner chaque décalage de mise en page individuel sur une page pour essayer de comprendre ce qui pourrait être à l'origine de l'instabilité et de résoudre le ou les problèmes.

Mesurer les décalages de mise en page

À l'aide de l'API Layout Instability, nous pouvons obtenir la liste de tous les événements de décalage de mise en page sur une page :

new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(list.getEntries().filter(entry => !entry.hadRecentInput));
  }).observe({type: "layout-shift", buffered: true});
}).then(console.log);

Cela génère un tableau de changements de mise en page qui ne sont pas précédés d'événements d'entrée :

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 210.78500000294298,
    "duration": 0,
    "value": 0.0001045969445437389,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

Dans cet exemple, un seul décalage très faible de 0,01 % a été détecté à 210 ms.

Connaître l'heure et la gravité du changement permet de déterminer plus facilement sa cause. Revenons à WebPageTest pour un environnement de laboratoire permettant d'effectuer d'autres tests.

Mesurer les changements de mise en page dans WebPageTest

Comme pour la mesure du CLS dans WebPageTest, la mesure des décalages de mise en page individuels nécessite une métrique personnalisée. Heureusement, le processus est plus simple maintenant que Chrome 77 est stable. L'API Layout Instability étant activée par défaut, vous devriez pouvoir exécuter cet extrait de code JS sur n'importe quel site Web dans Chrome 77 et obtenir des résultats immédiatement. Dans WebPageTest, vous pouvez utiliser le navigateur Chrome par défaut sans vous soucier des indicateurs de ligne de commande ni de l'utilisation de Canary.

Modifions donc ce script pour produire une métrique personnalisée pour WebPageTest :

[LayoutShifts]
return new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(JSON.stringify(list.getEntries().filter(entry => !entry.hadRecentInput)));
  }).observe({type: "layout-shift", buffered: true});
});

Dans ce script, la promesse se résout en une représentation JSON du tableau plutôt qu'en le tableau lui-même. En effet, les métriques personnalisées ne peuvent produire que des types de données primitifs, comme des chaînes ou des nombres.

Le site Web que j'utiliserai pour le test est ismyhostfastyet.com, un site que j'ai créé pour comparer les performances de chargement réelles des hébergeurs Web.

Identifier les causes de l'instabilité de la mise en page

Dans les résultats, nous pouvons voir que la métrique personnalisée "LayoutShifts" a la valeur suivante :

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3087.2349999990547,
    "duration": 0,
    "value": 0.3422101449275362,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

En résumé, un seul décalage de mise en page de 34,2 % se produit à 3 087 ms. Pour identifier le coupable, utilisons la vue en bande pellicule de WebPageTest.

Deux cellules de la pellicule montrent des captures d'écran avant et après le décalage de mise en page.
Deux cellules dans la pellicule, montrant des captures d'écran avant et après le décalage de mise en page.

En faisant défiler la vidéo jusqu'à environ 3 secondes, nous pouvons voir exactement la cause du décalage de mise en page de 34 % : le tableau coloré. Le site Web récupère de manière asynchrone un fichier JSON, puis l'affiche dans un tableau. Le tableau étant initialement vide, l'attente de son remplissage lorsque les résultats sont chargés provoque le décalage.

En-tête de police Web qui apparaît de nulle part.
En-tête de police Web apparaissant de manière inattendue.

Mais ce n'est pas tout. Lorsque la page est visuellement complète au bout de 4,3 secondes environ, nous pouvons voir que le <h1> de la page "Is my host fast yet?" (Mon hébergeur est-il rapide ?) apparaît de nulle part. Cela se produit parce que le site utilise une typographie Web et n'a pris aucune mesure pour optimiser le rendu. La mise en page ne semble pas changer lorsque cela se produit, mais l'expérience utilisateur est mauvaise si l'utilisateur doit attendre aussi longtemps pour lire le titre.

Corriger l'instabilité de la mise en page

Maintenant que nous savons que le tableau généré de manière asynchrone est à l'origine du décalage d'un tiers de la fenêtre d'affichage, il est temps de résoudre le problème. Nous ne connaissons pas le contenu de la table tant que les résultats JSON ne sont pas chargés. Toutefois, nous pouvons toujours la remplir avec des données de substitution afin que la mise en page elle-même soit relativement stable lorsque le DOM est rendu.

Voici le code permettant de générer des données d'espace réservé :

function getRandomFiller(maxLength) {
  var filler = '█';
  var len = Math.ceil(Math.random() * maxLength);
  return new Array(len).fill(filler).join('');
}

function getRandomDistribution() {
  var fast = Math.random();
  var avg = (1 - fast) * Math.random();
  var slow = 1 - (fast + avg);
  return [fast, avg, slow];
}

// Temporary placeholder data.
window.data = [];
for (var i = 0; i < 36; i++) {
  var [fast, avg, slow] = getRandomDistribution();
  window.data.push({
    platform: getRandomFiller(10),
    client: getRandomFiller(5),
    n: getRandomFiller(1),
    fast,
    avg,
    slow
  });
}
updateResultsTable(sortResults(window.data, 'fast'));

Les données de l'espace réservé sont générées de manière aléatoire avant d'être triées. Il inclut le caractère "█" répété un nombre aléatoire de fois pour créer des espaces réservés visuels pour le texte et une distribution générée aléatoirement des trois valeurs principales. J'ai également ajouté des styles pour désaturer toutes les couleurs du tableau afin d'indiquer clairement que les données ne sont pas encore entièrement chargées.

L'apparence des espaces réservés que vous utilisez n'a pas d'importance pour la stabilité de la mise en page. L'objectif des espaces réservés est d'assurer aux utilisateurs que du contenu sera bientôt disponible et que la page n'est pas cassée.

Voici à quoi ressemblent les espaces réservés pendant le chargement des données JSON :

Le tableau de données est affiché avec des données d&#39;espace réservé.
Le tableau de données est affiché avec des données d'espace réservé.

Il est beaucoup plus simple de résoudre le problème lié aux polices Web. Comme le site utilise Google Fonts, il nous suffit de transmettre la propriété display=swap dans la requête CSS. C'est tout. L'API Fonts ajoutera le style font-display: swap dans la déclaration de police, ce qui permettra au navigateur d'afficher immédiatement le texte dans une police de remplacement. Voici le balisage correspondant avec le correctif inclus :

<link href="https://fonts.googleapis.com/css?family=Chivo:900&display=swap" rel="stylesheet">

Vérifier les optimisations

Après avoir réexécuté la page dans WebPageTest, nous pouvons générer une comparaison avant et après pour visualiser la différence et mesurer le nouveau degré d'instabilité de la mise en page :

Bande de film WebPageTest montrant les deux sites se chargeant côte à côte avec et sans optimisations de la mise en page.
Bande de film WebPageTest montrant les deux sites se chargeant côte à côte avec et sans optimisation de la mise en page.
[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3070.9349999997357,
    "duration": 0,
    "value": 0.000050272187989256116,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

Selon la métrique personnalisée, un décalage de mise en page se produit toujours à 3 071 ms (à peu près au même moment qu'avant), mais l'ampleur du décalage est beaucoup plus faible : 0,005 %. Je peux m'en contenter.

La bande pellicule montre également clairement que la police <h1> est immédiatement remplacée par une police système, ce qui permet aux utilisateurs de la lire plus rapidement.

Conclusion

Les sites Web complexes connaîtront probablement beaucoup plus de changements de mise en page que dans cet exemple, mais le processus de correction reste le même : ajoutez des métriques d'instabilité de la mise en page à WebPageTest, croisez les résultats avec la bande vidéo de chargement visuel pour identifier les responsables et implémentez une correction à l'aide d'espaces réservés pour réserver l'espace à l'écran.

(Une dernière chose) Mesurer l'instabilité de la mise en page rencontrée par les utilisateurs réels

Il est agréable de pouvoir exécuter WebPageTest sur une page avant et après une optimisation, et de constater une amélioration d'une métrique. Toutefois, ce qui compte vraiment, c'est que l'expérience utilisateur s'améliore réellement. N'est-ce pas la raison pour laquelle nous essayons d'améliorer le site ?

L'idéal serait de commencer à mesurer l'instabilité de la mise en page des utilisateurs réels en plus de nos métriques de performances Web traditionnelles. Il s'agit d'un élément essentiel de la boucle de rétroaction sur l'optimisation, car les données provenant du terrain nous indiquent où se trouvent les problèmes et si nos correctifs ont eu un impact positif.

En plus de collecter vos propres données sur l'instabilité de la mise en page, consultez le rapport d'expérience utilisateur Chrome, qui inclut des données sur le Cumulative Layout Shift (CLS) issues de l'expérience utilisateur réelle sur des millions de sites Web. Il vous permet de découvrir vos performances (ou celles de vos concurrents) ou d'explorer l'état de l'instabilité de la mise en page sur le Web.