Débuguer et optimiser les Core Web Vitals

Comment mesurer, débuguer et optimiser les Core Web Vitals - les signaux web essentiels pour Google ? Voyons les différentes données que vous pouvez collecter (données de terrain et données de laboratoire), et comment vous pouvez les utiliser pour améliorer les performances  et l'expérience utilisateur de votre site web.

L’article original de Phil Walton est en anglais sur web.dev

Google propose actuellement deux catégories d'outils pour mesurer et optimiser les Web Vitals :

  • les outils de laboratoire (Lab data) tels que Lighthouse, où votre page web est chargée dans un environnement qui simule diverses conditions de navigation (réseau lent et appareil mobile bas de gamme) ;
  • les outils de terrain (Field data) tels que Chrome User eXperience report (CrUX), qui est basé sur des données agrégées d'utilisateurs réels de Chrome. Notez que les données de terrain signalées par des outils tels que PageSpeed ​​Insights et la Search Console proviennent de données CrUX.

Alors que les outils de terrain offrent des données plus précises - dans la mesure où elles traduisent l'expérience d’utilisateurs réels - les outils de laboratoire sont recommandés pour vous aider à identifier et à résoudre les problèmes de performance sur vos pages web.
(NdT. C’est la distinction entre Synthetic Monitoring et Real User Monitoring (RUM) sur lesquels vous trouverez plus de détails dans cet article).

Les données CrUX sont plus représentatives des performances réelles de votre page, mais il est peu probable que la connaissance de vos scores CrUX vous aide à trouver comment améliorer vos performances.

Lighthouse, quant à lui, vous permettra de générer un rapport pour vous aider à identifier les problèmes et liste des recommandations pour les résoudre, et optimiser votre vitesse de chargement. Cependant, Lighthouse ne fait que des suggestions pour les problèmes de performance qu'il découvre au moment du chargement de la page. Il ne détecte pas de problème qui pourrait se manifester uniquement à la suite d'une interaction de l'utilisateur, tel que le défilement ou le clic sur les boutons d’une page web.

C’est là qu’une question centrale se pose : comment capturer les informations nécessaires pour savoir comment obtenir une amélioration les métriques Web Vitals, auprès d'utilisateurs réels sur le terrain ?

Cet article explique en détail les APIs que vous pouvez utiliser pour collecter des informations supplémentaires pour une amélioration de chacune des métriques Core Web Vitals (CLS, LCP et FID), et indique des méthodes pour reporter ces données dans votre outil d'analyse existant.

 

Core Web Vitals : télécharger le dossier complet 

Core Web Vitals : les APIs pour détecter les problèmes de performance

Cumulative Layout Shift (CLS)

De toutes les métriques Core Web Vitals, le CLS est peut-être celle pour laquelle la collecte de données réelles est la plus importante. Le CLS est mesuré tout au long de la durée de vie de la page, de sorte que la façon dont un utilisateur interagit avec la page web (sur quelle distance il scrolle, sur quoi il clique, etc.) peut avoir un impact significatif sur les changements de mise en page et le déplacement des éléments dans le navigateur. Observez le rapport suivant de PageSpeed ​​Insights pour l'URL : web.dev/measure

PageSpeed Insights Google - CLS in Field vs. Lab data

La valeur Lab data rapportée par Lighthouse pour le CLS est différente de la valeur Field data rapportée par CrUX, et cela a du sens si vous considérez que la page web.dev/measure a beaucoup de contenu interactif, ce qui ne peut pas être apprécié par des tests Lighthouse.

A présent, même si vous intégrez le fait qu’une interaction de l'utilisateur affecte les données de terrain, vous avez besoin de savoir quels sont les éléments qui se déplacent sur la page pour mener à ce score de 0,45 au 75ème centile. Vous pouvez le faire grâce à l’interface LayoutShiftAttribution.

Cumulative Layout Shift : comment attribuer un changement de mise en page

L’interface LayoutShiftAttribution est exposée sur chaque entrée layout-shift émise par l’API Layout Instability.

Pour une explication détaillée de ces deux interfaces, vous pouvez consulter l’article “Debug layout shifts”. Le plus important à retenir est qu’il est possible d'observer chaque changement de mise en page qui se produit sur une page web, ainsi que les éléments qui se déplacent dans le navigateur.

Voici un exemple de code qui capture chaque changement de disposition (Layout Shifts), ainsi que les éléments concernés :

new PerformanceObserver((list) => {
 for (const {value, startTime, sources} of list.getEntries()) {
   // Log the shift amount and other entry info.
   console.log('Layout shift:', {value, startTime});
   if (sources) {
     for (const {node, curRect, prevRect} of sources) {
       // Log the elements that shifted.
       console.log('  Shift source:', node, {curRect, prevRect});
     }
   }
 }
}).observe({type: 'layout-shift', buffered: true});

Mesurer et envoyer des données à votre outil d'analyse pour chaque changement de mise en page qui se produit n’est pas souhaitable, cependant, en surveillant tous les changements, vous pouvez identifier ceux qui posent problème pour votre CLS, et donc pour votre expérience utilisateur, afin d' être en mesure de corriger et obtenir une amélioration.

Ainsi, l'objectif n'est pas d'identifier et de corriger chaque changement de mise en page qui se produit pour chaque utilisateur, mais plutôt d'identifier les changements qui affectent le plus grand nombre d'utilisateurs et qui contribuent ainsi le plus au score CLS de votre page au 75ème centile.

De plus, vous n'avez pas besoin de calculer le plus grand élément source à chaque fois qu'il y a un décalage, vous ne devez le faire que lorsque vous avez besoin d’envoyer la valeur CLS à votre outil d'analyse.

Le code suivant récupère une liste d’entrées layout-shift qui ont contribué au CLS, et renvoie le plus grand élément source du plus grand décalage :

function getCLSDebugTarget(entries) {
 const largestShift = entries.reduce((a, b) => {
   return a && a.value > b.value ? a : b;
 });
 if (largestShift && largestShift.sources) {
   const largestSource = largestShift.sources.reduce((a, b) => {
     return a.node && a.previousRect.width * a.previousRect.height >
         b.previousRect.width * b.previousRect.height ? a : b;
   });
   if (largestSource) {
     return largestSource.node;
   }
 }
}

Une fois que vous avez identifié l'élément le plus important contribuant au changement de mise en page le plus important, vous pouvez le signaler à votre outil d'analyse.

L'élément qui a le plus fort impact sur le Cumulative Layout Shift pour une page web donnée variera probablement d'un utilisateur à l'autre, mais si vous regroupez ces éléments pour tous les utilisateurs, vous serez en mesure de générer une liste d'éléments qui affectent le plus grand nombre d'utilisateurs.

Au fur et à mesure, une fois que vous avez identifié et corrigé la cause principale des changements de mise en page pour ces éléments, les plus petits changements deviendront les “pires” pour votre CLS, et finalement, tous les décalages signalés seront alors suffisamment minimes pour que vos pages web se situent dans le “bon” seuil de 0,1 !

Certaines métadonnées peuvent être utiles à capturer avec le plus grand élément source de décalage :

  • le moment où se produit le plus grand décalage de mise en page ;
  • l'URL au moment où le plus grand changement de mise en page se produit (pour les sites web qui mettent à jour dynamiquement les URLs, comme c’est le cas pour les Single Page Applications).

Largest Contentful Paint (LCP)

Pour une amélioration du LCP à partir de données de terrain, vous avez besoin de savoir quel élément précis est considéré comme le plus grand élément de la page lors du chargement (l'élément candidat au LCP).

Notez qu'il est tout à fait possible - en fait, c'est assez courant - que l'élément candidat au LCP soit différent d'un utilisateur à l'autre pour une même page.

Il y a plusieurs raisons à cela :

  • Les appareils des utilisateurs ont des résolutions d'écran différentes, ce qui entraîne différentes mises en page et donc différents éléments visibles dans la fenêtre.
  • Les utilisateurs ne chargent pas toujours les pages scrollées jusqu'en haut. Souvent, les liens contiennent des identificateurs de fragments, ou même des fragments de texte, ce qui fait qu'il est possible que vos pages soient chargées et affichées à des positions différentes selon les utilisateurs.
  • Le contenu peut être personnalisé pour un utilisateur donné, de sorte que l'élément candidat au LCP peut varier d'un utilisateur à l'autre.

Pour ces raisons, vous ne pouvez pas faire d’hypothèses précises et définitives sur l’élément candidat au LCP pour tous vos utilisateurs, mais vous devez le mesurer en fonction du comportement d'utilisateurs réels.

Identifiez l'élément candidat au LCP

Pour déterminer l'élément candidat au LCP en JavaScript, vous pouvez utiliser l’API Largest Contentful Paint, la même API que vous utilisez pour déterminer la valeur de temps LCP.

A partir d'une liste d’entrées largest-contentful-paint, vous pouvez déterminer l'élément candidat au LCP en observant la dernière entrée :

function getLCPDebugTarget(entries) {
 const lastEntry = entries[entries.length - 1];
 return lastEntry.element;
}

Attention : comme expliqué à propos de la métrique LCP, l'élément candidat au LCP peut changer lors du chargement de la page, ce qui implique des investigations un peu plus poussées pour identifier l'élément candidat au LCP "final". Le moyen le plus simple d'identifier et de mesurer l'élément candidat au LCP “final” consiste à utiliser la bibliothèque JavaScript web-vitals, comme illustré dans l’exemple que nous allons détailler ci-après.

Une fois que vous connaissez l'élément candidat au LCP, vous pouvez l'envoyer à votre outil d'analyse avec la valeur de la métrique. Comme avec le CLS, vous pourrez alors identifier les éléments les plus importants à optimiser en premier.

Voici quelques métadonnées qu'il peut être utile de capturer avec l'élément candidat au LCP :

  • l'URL de la source de l'image (si l'élément candidat au LCP est une image) ;
  • la famille de fonts (si l'élément candidat LCP est du texte et que la page utilise des fonts Web).

First Input Delay (FID)

Pour optimiser le FID à partir de données de terrain, il est important de se rappeler que le FID ne mesure que le délai de latence globale suite à la première interaction d’un utilisateur. Cela signifie que ce avec quoi l'utilisateur a interagi n'est pas forcément prioritaire dans le Main Thread au moment même où il interagit.

Par exemple, de nombreuses applications JavaScript prenant en charge le rendu côté serveur (Server Side Rendering) fourniront du HTML statique qui peut être rendu à l'écran avant qu'il ne soit réactif au clic de l'utilisateur, c'est-à-dire avant que le JavaScript requis pour rendre le contenu interactif ait fini de se charger.

Pour ces types d'applications, il peut être très important de savoir si la première entrée, ou interaction, s'est produite avant ou après l'hydratation. S'il s'avère que de nombreuses personnes tentent d'interagir avec la page avant la fin de l'hydratation, prévoyez de rendre vos pages web dans un état désactivé ou de chargement, plutôt que dans un état qui semble interactif.

Si votre infrastructure d'application indique l’horodatage d’hydratation, vous pouvez facilement le comparer avec l'horodatage de l’entrée first-input pour déterminer si la première entrée s'est produite avant ou après l'hydratation. Si votre framework n’indique pas cet horodatage ou n'utilise pas du tout d’hydratation, un autre signal utile peut être de savoir si l'entrée a eu lieu avant ou après la fin du chargement du JavaScript.

L’événement DOMContentLoaded se déclenche une fois que le code HTML de la page web a été complètement chargé et analysé, ce qui inclut l’attente du chargement de tout script synchrone, différé, ou de module (y compris tous les modules importés statiquement). Vous pouvez donc utiliser le moment de cet événement et le comparer au moment où le FID s'est produit.

Le code suivant prend une entrée first-input et renvoie “true” si la première entrée s'est produite avant la fin de l'événement DOMContentLoaded :

function wasFIDBeforeDCL(fidEntry) {
 const navEntry = performance.getEntriesByType('navigation')[0];
 return navEntry && fidEntry.startTime < navEntry.domContentLoadedEventStart;
}

Si votre page utilise des scripts async ou des déclarations import() dynamiques pour charger des JavaScript, l’événement DOMContentLoaded peut ne pas être un signal pertinent. Au lieu de cela, vous pouvez envisager d'utiliser l’événement load ou, s'il existe un script particulier dont vous savez qu'il prend un certain temps à s'exécuter, vous pouvez utiliser l’entrée Resource Timing pour ce script.

Identifiez l'élément cible pour le FID 

Un autre signal utile pour optimiser le First Input Delay est l’élément avec lequel l’utilisateur interagit. Bien que l'interaction avec l'élément lui-même ne contribue pas au FID (rappelez-vous que le FID n'est que le temps d’attente dans la latence totale des événements), savoir avec quels éléments vos utilisateurs interagissent est utile pour déterminer la meilleure façon d'améliorer le FID.

Par exemple, si la grande majorité des premières interactions de votre utilisateur se font sur un élément particulier, vous pouvez envisager d'inclure le code JavaScript nécessaire pour cet élément dans le HTML, et lazyloader le reste.Pour obtenir l'élément associé à la première interaction, vous pouvez référencer la propriété target de l’entrée first-input :

function getFIDDebugTarget(entries) {
 return entries[0].target;
}

Voici quelques métadonnées qu'il peut être utile de capturer avec l'élément target du FID :

  • le type d'événement (tels que mousedown, keydown, pointerdown) ;
  • toute donnée d'attribution de Long Task pertinente pour une tâche longue qui s'est produite en même temps que la première interaction ou entrée (utile si la page charge des scripts tiers).

Comment utiliser la bibliothèque JavaScript Web-Vitals 

Les sections ci-dessus suggèrent des données à inclure dans celles que vous envoyez à votre outil d'analyse pour optimiser vos Core Web Vitals. 

Chacun des exemples associe la valeur de la métrique Web Vitals récupérée en JavaScript en exploitant l’objet performance avec un élément DOM qui peut être utilisé pour résoudre les problèmes affectant cette métrique.

Ces exemples sont prévus pour fonctionner avec la bibliothèque JavaScript web-vitals, qui expose la liste des entrées de performance sur l’objet Metric passé à chaque fonction de rappel.

Si vous combinez les exemples énumérés ci-dessus avec les fonctions web-vitals, le résultat ressemblera à ceci :

import {getLCP, getFID, getCLS} from 'web-vitals';
function getSelector(node, maxLen = 100) {
 let sel = '';
 try {
   while (node && node.nodeType !== 9) {
     const part = node.id ? '#' + node.id : node.nodeName.toLowerCase() + (
       (node.className && node.className.length) ?
       '.' + Array.from(node.classList.values()).join('.') : '');
     if (sel.length + part.length > maxLen - 1) return sel || part;
     sel = sel ? part + '>' + sel : part;
     if (node.id) break;
     node = node.parentNode;
   }
 } catch (err) {
   // Do nothing...
 }
 return sel;
}
function getLargestLayoutShiftEntry(entries) {
 return entries.reduce((a, b) => a && a.value > b.value ? a : b);
}
function getLargestLayoutShiftSource(sources) {
 return sources.reduce((a, b) => {
   return a.node && a.previousRect.width * a.previousRect.height >
       b.previousRect.width * b.previousRect.height ? a : b;
 });
}
function wasFIDBeforeDCL(fidEntry) {
 const navEntry = performance.getEntriesByType('navigation')[0];
 return navEntry && fidEntry.startTime < navEntry.domContentLoadedEventStart;
}
function getDebugInfo(name, entries = []) {
 // In some cases there won't be any entries (e.g. if CLS is 0,
 // or for LCP after a bfcache restore), so we have to check first.
 if (entries.length) {
   if (name === 'LCP') {
     const lastEntry = entries[entries.length - 1];
     return {
       debug_target: getSelector(lastEntry.element),
       event_time: lastEntry.startTime,
     };
   } else if (name === 'FID') {
     const firstEntry = entries[0];
     return {
       debug_target: getSelector(firstEntry.target),
       debug_event: firstEntry.name,
       debug_timing: wasFIDBeforeDCL(firstEntry) ? 'pre_dcl' : 'post_dcl',
       event_time: firstEntry.startTime,
     };
   } else if (name === 'CLS') {
     const largestEntry = getLargestLayoutShiftEntry(entries);
     if (largestEntry && largestEntry.sources) {
       const largestSource = getLargestLayoutShiftSource(largestEntry.sources);
       if (largestSource) {
         return {
           debug_target: getSelector(largestSource.node),
           event_time: largestEntry.startTime,
         };
       }
     }
   }
 }
 // Return default/empty params in case there are no entries.
 return {
   debug_target: '(not set)',
 };
}
function sendToAnalytics({name, value, entries}) {
 navigator.sendBeacon('/analytics', JSON.stringify({
   name,
   value,
   ...getDebugInfo(name, entries)
 });
}
getLCP(sendToAnalytics);
getFID(sendToAnalytics);
getCLS(sendToAnalytics);

Le format spécifique requis pour envoyer les données varie selon l'outil d'analyse, mais le code ci-dessus doit être suffisant pour obtenir les données nécessaires, quelles que soient les contraintes en termes de format.

Notez que le code ci-dessus comprend également une fonction getSelector() (non mentionnée dans les sections précédentes), qui prend un nœud DOM et renvoie un sélecteur CSS représentant ce nœud et sa place dans le DOM. Il relève également un paramètre de longueur maximale facultatif (par défaut à 100 caractères) dans le cas où votre outil d'analyse impose des restrictions de longueur sur les données que vous lui envoyez.

Core Web Vitals : comment obtenir un rapport et visualiser les données

Une fois que vous avez collecté vos métriques Web Vitals, l'étape suivante consiste à regrouper les données de tous vos utilisateurs pour rechercher des modèles et des tendances.

Comme mentionné ci-dessus, vous n'avez pas nécessairement besoin de résoudre absolument chaque problème rencontré par vos utilisateurs.

Vous avez besoin de résoudre, en particulier au début, les problèmes qui affectent le plus grand nombre d'utilisateurs et qui ont un impact négatif sur vos scores Core Web Vitals.

L'outil Web Vitals Report

Si vous utilisez l’outil Web Vitals Report, il permet de prendre en charge la création de rapport sur une seule dimension de débogage pour chacune des métriques Core Web Vitals.

Voici une capture d’écran de la section d'informations de débogage de Web Vitals Report, montrant les données du site web de l'outil de Web Vitals Report lui-même :

Web Vitals Report

En utilisant les données ci-dessus, vous pouvez voir que ce qui cause le déplacement de l’élément section.Intro a le plus fort impact sur le CLS sur cette page. Ainsi, identifier et corriger la cause de ce décalage sera le plus pertinent pour améliorer le score.

Mesurer et optimiser les Core Web Vitals : les signaux web essentiels en résumé

Espérons que cet article vous a aidé à comprendre comment utiliser les APIs de performance existantes pour obtenir des informations utiles pour une amélioration de chacune des métriques Core Web Vitals basées sur les interactions des utilisateurs réels. Bien que ces techniques soient axées sur les Core Web Vitals, les concepts s'appliquent également à l’optimisation de toute métrique de performance web mesurable en JavaScript.

Si vous commencez tout juste à mesurer vos performances et que vous êtes déjà un utilisateur de Google Analytics, l'outil Web Vitals Report peut être un bon point de départ, car il prend déjà en charge la création de rapports sur les informations de débogage pour chacune des métriques Core Web Vitals.

Si vous êtes un fournisseur d'analyse et que vous cherchez à améliorer vos produits et à fournir plus d'informations de débogage à vos utilisateurs, vous pouvez vous inspirer des techniques décrites, mais ne vous limitez pas aux seules idées présentées ici ! Cet article est destiné à être applicable à tous les outils d'analyse, cependant, les outils d'analyse individuels peuvent (et devraient) probablement capturer et remonter encore plus de données utiles à l’optimisation des Core Web Vitals.

Enfin, si vous pensez manquer de fonctionnalités ou d'informations pour optimiser ces métriques, envoyez vos commentaires à web-vitals-feedback@googlegroups.com.

Vous voulez plus de détails sur le CLS, le LCP et le FID,
et connaître les techniques pour optimiser ces signaux SEO ?

Core Web Vitals : télécharger le dossier complet 


Hello SMX Paris !