Identifier les interactions lentes sur le terrain

Découvrez comment identifier les interactions lentes dans les données réelles de votre site Web afin d'identifier des opportunités d'amélioration de son rapport "Interaction to Next Paint".

Les données des champs sont des données qui vous indiquent comment les utilisateurs réels consultent votre site Web. Il identifie les problèmes que vous ne trouvez pas uniquement dans les données expérimentales. En ce qui concerne l'Interaction to Next Paint (INP), les données de terrain sont essentielles pour identifier les interactions lentes et fournissent des indices essentiels pour vous aider à les corriger.

Dans ce guide, vous apprendrez à évaluer rapidement l'INP de votre site Web à l'aide des données réelles du rapport d'expérience utilisateur Chrome (CrUX, User Experience Report) afin de déterminer si votre site Web présente des problèmes avec INP. Par la suite, vous apprendrez à utiliser le build d'attribution de la bibliothèque JavaScript web-Vitals et les nouveaux insights qu'elle fournit de l'API Long Animation Frames (LoAF) pour collecter et interpréter les données de champ pour les interactions lentes sur votre site Web.

Commencez avec CrUX pour évaluer l'INP de votre site Web

Si vous ne collectez pas de données réelles auprès des utilisateurs de votre site Web, l'expérience utilisateur Chrome (CrUX) peut constituer un bon point de départ. L'équipe CrUX collecte des données réelles auprès d'utilisateurs Chrome réels qui ont accepté d'envoyer des données de télémétrie.

Les données d'expérience utilisateur Chrome (CrUX) apparaissent dans différents domaines, selon la portée des informations que vous recherchez. CrUX peut fournir des données sur INP et d'autres Core Web Vitals pour:

  • Des pages individuelles et des origines entières à l'aide de PageSpeed Insights
  • Types de pages. Par exemple, de nombreux sites Web d'e-commerce proposent des types "Page d'informations détaillées sur le produit" et "Page d'informations détaillées sur le produit". Vous pouvez obtenir des données CrUX pour des types de pages uniques dans la Search Console.

Pour commencer, vous pouvez saisir l'URL de votre site Web dans PageSpeed Insights. Une fois l'URL saisie, les données de ses champs (le cas échéant) s'affichent pour plusieurs métriques, y compris l'INP. Vous pouvez également utiliser les boutons d'activation/de désactivation pour vérifier vos valeurs INP pour les dimensions pour mobile et ordinateur.

Données de terrain affichées par CrUX dans PageSpeed Insights, indiquant le LCP, l'INP et le CLS pour les trois Core Web Vitals, et le TTFB, le FCP en tant que métriques de diagnostic et le FID en tant que métrique Core Web Vitals obsolète.
Lecture des données CrUX telles qu'elles apparaissent dans PageSpeed Insights. Dans cet exemple, l'INP de la page Web donnée doit être amélioré.

Ces données sont utiles car elles vous indiquent si vous avez un problème. Cependant, l'expérience utilisateur Chrome ne peut pas vous indiquer ce qui est à l'origine du problème. Il existe de nombreuses solutions de surveillance des utilisateurs réels (RUM, Real User Monitoring) qui vous permettent de recueillir vos propres données de terrain auprès des utilisateurs de votre site Web pour vous aider à répondre à cette question. L'une des options consiste à rassembler ces données de champ vous-même à l'aide de la bibliothèque JavaScript web-vitals.

Collecter des données de champ avec la bibliothèque JavaScript web-vitals

La bibliothèque JavaScript web-vitals est un script que vous pouvez charger sur votre site Web pour recueillir des données réelles provenant de ses utilisateurs. Vous pouvez l'utiliser pour enregistrer un certain nombre de métriques, y compris INP dans les navigateurs compatibles.

Navigateurs pris en charge

  • 96
  • 96
  • x
  • x

Source

Le build standard de la bibliothèque web-vitals peut être utilisé pour obtenir des données INP de base auprès des utilisateurs sur le terrain:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  console.log(name);    // 'INP'
  console.log(value);   // 512
  console.log(rating);  // 'poor'
});

Afin d'analyser les données de terrain de vos utilisateurs, vous devez envoyer ces données quelque part:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  // Prepare JSON to be sent for collection. Note that
  // you can add anything else you'd want to collect here:
  const body = JSON.stringify({name, value, rating});

  // Use `sendBeacon` to send data to an analytics endpoint.
  // For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
  navigator.sendBeacon('/analytics', body);
});

Cependant, ces données en elles-mêmes ne vous en disent pas beaucoup plus que l'expérience utilisateur Chrome (CrUX). C'est là qu'intervient la génération d'attributions de la bibliothèque web-vitals.

Aller plus loin avec le build d'attribution de la bibliothèque web-vitals

Le modèle d'attribution de la bibliothèque web-Vitals affiche des données supplémentaires que vous pouvez obtenir auprès des utilisateurs sur le terrain pour vous aider à mieux résoudre les problèmes liés aux interactions problématiques qui affectent l'INP de votre site Web. Ces données sont accessibles via l'objet attribution affiché dans la méthode onINP() de la bibliothèque:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, rating, attribution}) => {
  console.log(name);         // 'INP'
  console.log(value);        // 56
  console.log(rating);       // 'good'
  console.log(attribution);  // Attribution data object
});
Mode d'affichage des journaux de la console issus de la bibliothèque web-vitals. Dans cet exemple, la console affiche le nom de la métrique (INP), la valeur INP (56), où cette valeur se situe dans les seuils INP (bonne) et les différentes informations affichées dans l'objet d'attribution, y compris les entrées de l'API Long Animation Frame.
Affichage des données de la bibliothèque web-vitals dans la console.

En plus de l'INP de la page elle-même, la création de l'attribution fournit de nombreuses données que vous pouvez utiliser pour vous aider à comprendre les raisons de la lenteur des interactions, y compris sur quelle partie de l'interaction vous devez vous concentrer. Il peut vous aider à répondre à des questions importantes, telles que:

  • "L'utilisateur a-t-il interagi avec la page pendant son chargement ?"
  • "Les gestionnaires d'événements de l'interaction se sont-ils exécutés depuis longtemps ?"
  • "Le démarrage du code du gestionnaire d'événements d'interaction a-t-il été retardé ? Si oui, qu'était-ce qui se passait d'autre sur le thread principal à ce moment-là ? »
  • "L'interaction a-t-elle entraîné un grand nombre de tâches de rendu qui ont retardé l'application du cadre suivant ?"

Le tableau suivant présente certaines des données d'attribution de base que vous pouvez obtenir à partir de la bibliothèque et qui peuvent vous aider à identifier les principales causes de la lenteur des interactions sur votre site Web:

Clé d'objet attribution Données
interactionTarget Un sélecteur CSS pointant vers l'élément qui a généré la valeur INP de la page (par exemple, button#save)
interactionType Type d'interaction (clics, appuis ou saisies au clavier).
inputDelay* Le délai d'entrée de l'interaction
processingDuration* Délai entre le moment où le premier écouteur d'événements a démarré en réponse à l'interaction de l'utilisateur et le moment où le traitement de l'ensemble des écouteurs d'événements est terminé.
presentationDelay* Le délai de présentation de l'interaction, qui survient entre le moment où les gestionnaires d'événements se terminent et le moment où l'image suivante est affichée.
longAnimationFrameEntries* Entrées du LoAF associé à l'interaction. Pour en savoir plus, consultez la section suivante.
*Nouveau dans la version 4

À partir de la version 4 de la bibliothèque web-vitals, vous pouvez obtenir des informations encore plus détaillées sur les interactions problématiques grâce aux données qu'elle fournit avec les répartitions de phase INP (délai d'entrée, durée de traitement et délai de présentation) et l'API Long Animation Frame (LoAF).

API LoAF (Long Animation Frame)

Navigateurs pris en charge

  • 123
  • 123
  • x
  • x

Source

Déboguer les interactions à l'aide des données de terrain n'est pas une mince affaire. Toutefois, grâce aux données du MdF, il est désormais possible d'obtenir des informations plus précises sur les causes des interactions lentes, car le LoAF expose un certain nombre de codes temporels détaillés et d'autres données que vous pouvez utiliser pour identifier les causes précises, et surtout, où se trouve la source du problème dans le code de votre site Web.

Le build d'attribution de la bibliothèque web-vitals expose un tableau d'entrées LoAF sous la clé longAnimationFrameEntries de l'objet attribution. Le tableau suivant répertorie quelques bits de données clés que vous pouvez trouver dans chaque entrée LoAF:

Clé d'objet d'entrée LoAF Données
duration Durée de l'image longue de l'animation, jusqu'à la fin de la mise en page, à l'exclusion de la peinture et de la composition.
blockingDuration Temps total dans le frame pendant lequel le navigateur n'a pas pu répondre rapidement en raison de longues tâches. Ce temps de blocage peut inclure des tâches longues exécutant JavaScript, ainsi que toute longue tâche d'affichage ultérieure dans le frame.
firstUIEventTimestamp Code temporel correspondant au moment où l'événement a été mis en file d'attente pendant le frame. Utile pour déterminer le début du délai d'entrée d'une interaction.
startTime Le code temporel de début de la trame.
renderStart Date à laquelle le rendu de l'image a commencé. Cela inclut tous les rappels requestAnimationFrame (et les rappels ResizeObserver, le cas échéant), mais éventuellement avant le début des tâches de style/mise en page.
styleAndLayoutStart En cas de modification du style ou de la mise en page dans le cadre. Cela peut être utile pour déterminer la durée du travail de style/mise en page lorsque d'autres codes temporels sont disponibles.
scripts Tableau d'éléments contenant des informations d'attribution de script contribuant à l'INP de la page.
Visualisation d'une longue image d'animation selon le modèle LoAF.
Schéma des codes temporels d'une longue image d'animation selon l'API LoAF (moins blockingDuration).

Toutes ces informations peuvent vous en dire beaucoup sur ce qui ralentit une interaction, mais le tableau scripts que les entrées LoAF génèrent doit être particulièrement intéressant:

Clé d'objet d'attribution de script Données
invoker Le demandeur. Cela peut varier en fonction du type de demandeur décrit à la ligne suivante. Il peut s'agir de valeurs telles que 'IMG#id.onload', 'Window.requestAnimationFrame' ou 'Response.json.then'.
invokerType Type de demandeur. Il peut s'agir de 'user-callback', 'event-listener', 'resolve-promise', 'reject-promise', 'classic-script' ou 'module-script'.
sourceURL URL du script d'où provient l'image longue de l'animation.
sourceCharPosition Position du caractère dans le script identifié par sourceURL.
sourceFunctionName Nom de la fonction dans le script identifié.

Chaque entrée de ce tableau contient les données présentées dans ce tableau, qui vous donnent des informations sur le script à l'origine de l'interaction lente et sur la manière dont il l'a été.

Mesurez et identifiez les causes courantes des interactions lentes

Pour vous donner une idée de la manière dont vous pourriez utiliser ces informations, ce guide vous explique comment utiliser les données LoAF affichées dans la bibliothèque web-vitals pour déterminer certaines causes de la lenteur des interactions.

Temps de traitement longs

La durée de traitement d'une interaction correspond au temps nécessaire à l'exécution complète des rappels du gestionnaire d'événements enregistrés de l'interaction, ainsi qu'à toute autre action pouvant se produire entre elles. La bibliothèque web-vitals met en évidence les durées de traitement élevées:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5
});

Il est naturel de penser que la cause principale d'une interaction lente est que le code de votre gestionnaire d'événements a pris trop de temps à s'exécuter, mais ce n'est pas toujours le cas. Une fois que vous avez confirmé que c'est le problème, vous pouvez approfondir les données du LoAF:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5

  // Get the longest script from LoAF covering `processingDuration`:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Get attribution for the long-running event handler:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Comme vous pouvez le voir dans l'extrait de code précédent, vous pouvez utiliser les données du LoAF pour identifier la cause précise d'une interaction avec des valeurs de durée de traitement élevées, y compris:

  • Élément et son écouteur d'événements enregistré.
  • Le fichier de script (et la position des caractères qu'il contient) contenant le code du gestionnaire d'événements de longue durée
  • Nom de la fonction.

Ce type de données est inestimable. Vous n'avez plus besoin d'effectuer le travail nécessaire pour déterminer exactement quelle interaction (ou quels gestionnaires d'événements) étaient responsables de valeurs de durée de traitement élevées. De plus, étant donné que les scripts tiers peuvent souvent enregistrer leurs propres gestionnaires d'événements, vous pouvez déterminer si c'est votre code qui en était responsable. Pour le code sur lequel vous avez le contrôle, nous vous conseillons d'optimiser les longues tâches.

Retards de saisie longs

Bien que les gestionnaires d'événements de longue durée soient courants, il existe d'autres parties de l'interaction à prendre en compte. Une partie se produit avant le délai de traitement, appelé délai d'entrée. Il s'agit du délai entre le moment où l'utilisateur lance l'interaction et le moment où les rappels du gestionnaire d'événements commencent à s'exécuter et se produit lorsque le thread principal traite déjà une autre tâche. Le build d'attribution de la bibliothèque web-vitals peut vous indiquer la durée du délai d'entrée pour une interaction:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536
});

Si vous remarquez que certaines interactions présentent des délais de saisie élevés, vous devez déterminer ce qui se passait sur la page au moment de l'interaction à l'origine du long délai de saisie. Cela se résume souvent à ce que l'interaction s'est produite pendant le chargement de la page, ou après.

Était-ce pendant le chargement de la page ?

Le thread principal est souvent plus occupé pendant le chargement d'une page. Pendant ce temps, toutes sortes de tâches sont mises en file d'attente et traitées, et si l'utilisateur essaie d'interagir avec la page pendant que toutes ces tâches sont en cours, cela peut retarder l'interaction. Les pages qui chargent beaucoup de code JavaScript peuvent démarrer la compilation et l'évaluation des scripts, ainsi que l'exécution de fonctions pour préparer une page aux interactions des utilisateurs. Cette tâche peut gêner l'interaction de l'utilisateur lorsque cette activité se produit, et vous pouvez savoir si c'est le cas pour les utilisateurs de votre site Web:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Invoker types can describe if script eval blocked the main thread:
    const {invokerType} = script;    // 'classic-script' | 'module-script'
    const {sourceLocation} = script; // 'https://example.com/app.js'
  }
});

Si vous enregistrez ces données dans le champ et que vous constatez des délais d'entrée élevés et des types de demandeurs 'classic-script' ou 'module-script' élevés, il est normal d'affirmer que les scripts de votre site prennent beaucoup de temps à évaluer et qu'ils bloquent le thread principal suffisamment longtemps pour retarder les interactions. Pour réduire ce temps de blocage, vous pouvez diviser vos scripts en groupes plus petits, différer le chargement initial du code inutilisé à une date ultérieure et vérifier que votre site ne contient pas de code inutilisé que vous pouvez supprimer complètement.

Était-ce après le chargement de la page ?

Bien que les délais de saisie se produisent souvent pendant le chargement d'une page, il est tout aussi possible qu'ils se produisent après le chargement de la page, pour une cause complètement différente. Le code qui s'exécute périodiquement à la suite d'un appel setInterval antérieur, voire les rappels d'événements mis en file d'attente avant le chargement, encore en cours de traitement, peuvent être à l'origine des retards d'entrée après le chargement de la page.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    const {invokerType} = script;        // 'user-callback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Comme c'est le cas pour le dépannage des valeurs de durée de traitement élevées, les retards d'entrée élevés engendrés par les causes mentionnées précédemment vous permettront d'obtenir des données détaillées sur l'attribution des scripts. Toutefois, la différence réside dans le fait que le type de demandeur change en fonction de la nature du travail qui a retardé l'interaction:

  • 'user-callback' indique que la tâche bloquante provenait de setInterval, setTimeout ou même requestAnimationFrame.
  • 'event-listener' indique que la tâche bloquante provient d'une entrée antérieure qui était en file d'attente et est toujours en cours de traitement.
  • 'resolve-promise' et 'reject-promise' signifient que la tâche bloquante provenait d'une tâche asynchrone lancée précédemment, et résolue ou refusée au moment où l'utilisateur a tenté d'interagir avec la page, ce qui a retardé l'interaction.

Dans tous les cas, les données d'attribution du script vous indiqueront où commencer à chercher et si le délai d'entrée est dû à votre propre code ou à celui d'un script tiers.

Présentation trop lente

Les délais de présentation correspondent au dernier kilomètre d'une interaction. Ils commencent lorsque les gestionnaires d'événements de l'interaction se terminent, jusqu'au moment où le cadre suivant a été affiché. Elles se produisent lorsque le travail dans un gestionnaire d'événements dû à une interaction modifie l'état visuel de l'interface utilisateur. Comme pour les durées de traitement et les délais d'entrée, la bibliothèque web-Vitals peut vous indiquer le délai de présentation d'une interaction:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691
});

Si vous enregistrez ces données et constatez des retards de présentation élevés pour les interactions contribuant à l'INP de votre site Web, les causes peuvent varier, mais voici quelques causes à surveiller.

Coûts liés au style et à la mise en page

Les retards de présentation longs peuvent s'avérer coûteux en termes de recalcul de style et de mise en page. Cette opération peut avoir plusieurs causes, y compris les sélecteurs CSS complexes et les tailles de DOM volumineuses. Vous pouvez mesurer la durée de ce travail grâce aux durées LoAF affichées dans la bibliothèque web-vitals:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get necessary timings:
  const {startTime} = loaf; // 2120.5
  const {duration} = loaf;  // 1002

  // Figure out the ending timestamp of the frame (approximate):
  const endTime = startTime + duration; // 3122.5

  // Get the start timestamp of the frame's style/layout work:
  const {styleAndLayoutStart} = loaf; // 3011.17692309

  // Calculate the total style/layout duration:
  const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running style and layout operation:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

LoAF ne vous indiquera pas la durée du travail de style et de mise en page pour un cadre, mais il vous indiquera quand il a commencé. Avec cet horodatage de début, vous pouvez utiliser d'autres données de la LoAF pour calculer une durée précise de ce travail en déterminant l'heure de fin du frame et en soustrayant le code temporel de début du travail de style et de mise en page à cette valeur.

Rappels requestAnimationFrame de longue durée

L'une des causes potentielles de longs retards de présentation est un travail excessif effectué dans un rappel requestAnimationFrame. Le contenu de ce rappel est exécuté une fois l'exécution des gestionnaires d'événements terminée, mais juste avant le recalcul du style et la mise en page.

Ces rappels peuvent prendre beaucoup de temps si le travail qu'ils contiennent est complexe. Si vous pensez que des valeurs de délai de présentation élevées sont dues à un travail que vous effectuez avec requestAnimationFrame, vous pouvez utiliser les données LoAF affichées par la bibliothèque web-vitals pour identifier les scénarios suivants:

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 543.1999999880791

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get the render start time and when style and layout began:
  const {renderStart} = loaf;         // 2489
  const {styleAndLayoutStart} = loaf; // 2989.5999999940395

  // Calculate the `requestAnimationFrame` callback's duration:
  const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running requestAnimationFrame callback:
    const {invokerType} = script;        // 'user-callback'
    const {invoker} = script;            // 'FrameRequestCallback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Si vous constatez qu'une grande partie du délai de présentation est consacré à un rappel requestAnimationFrame, assurez-vous que le travail que vous effectuez dans ces rappels se limite à effectuer un travail qui entraîne une mise à jour réelle de l'interface utilisateur. Toute autre tâche qui n'affecte pas le DOM ou qui modifie les styles retardera inutilement l'affichage de l'image suivante. Par conséquent, faites attention !

Conclusion

Les données de terrain sont la meilleure source d'informations sur laquelle vous pouvez vous appuyer pour comprendre quelles interactions sont problématiques pour les utilisateurs réels sur le terrain. En utilisant des outils de collecte de données sur le terrain tels que la bibliothèque JavaScript web-Vitals (ou un fournisseur RUM), vous pouvez être plus sûr des interactions qui posent le plus de problèmes, puis passer à la recréation des interactions problématiques dans l'atelier avant de les corriger.

Image principale tirée de Unsplash, de Federico Respini.