Identifier les interactions lentes sur le terrain

Découvrez comment identifier les interactions lentes dans les données de votre site Web afin de trouver des possibilités d'amélioration de son temps d'interaction avant la prochaine peinture.

Les données sur le terrain vous indiquent comment les utilisateurs réels interagissent avec votre site Web. Il met en évidence les problèmes que vous ne pouvez pas trouver uniquement dans les données de laboratoire. En ce qui concerne l'Interaction to Next Paint (INP), les données sur le terrain sont essentielles pour identifier les interactions lentes et fournir des indices essentiels pour les résoudre.

Dans ce guide, vous allez découvrir comment évaluer rapidement l'INP de votre site Web à l'aide des données sur le terrain du rapport d'expérience utilisateur Chrome pour déterminer s'il présente des problèmes d'INP. Vous apprendrez ensuite à utiliser la version d'attribution de la bibliothèque JavaScript Web Vitals et les nouveaux insights qu'elle fournit à partir de l'API Long Animation Frames (LoAF) pour collecter et interpréter les données sur le terrain pour les interactions lentes sur votre site Web.

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

Si vous ne collectez pas de données d'utilisation réelles auprès des utilisateurs de votre site Web, CrUX peut être un bon point de départ. CrUX collecte des données de champ auprès d'utilisateurs Chrome réels qui ont accepté d'envoyer des données de télémétrie.

Les données CrUX s'affichent dans plusieurs zones différentes, en fonction de la portée des informations que vous recherchez. CrUX peut fournir des données sur l'INP et d'autres métriques 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 de pages "Page d'informations détaillées sur les produits" et "Page de fiche 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 champ correspondantes (si elles sont disponibles) 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 mobiles et ordinateurs.

Données sur le terrain telles qu'elles apparaissent dans CrUX dans PageSpeed Insights, montrant les trois métriques Core Web Vitals : LCP, INP et CLS, ainsi que les métriques de diagnostic TTFB et FCP, et la métrique Core Web Vitals obsolète FID.
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 rencontrez un problème. CrUX ne peut toutefois pas vous indiquer ce qui cause les problèmes. De nombreuses solutions de surveillance des utilisateurs réels (RUM) sont disponibles pour vous aider à collecter vos propres données de champ auprès des utilisateurs de votre site Web. Vous pouvez par exemple le faire vous-même à l'aide de la bibliothèque JavaScript Web Vitals.

Collecter des données sur le terrain 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 collecter des données sur le terrain auprès des utilisateurs de votre site. Vous pouvez l'utiliser pour enregistrer un certain nombre de métriques, y compris l'INP dans les navigateurs compatibles.

Navigateurs pris en charge

  • Chrome: 96.
  • Edge: 96.
  • Firefox: non compatible.
  • Safari: non compatible.

Source

La version standard de la bibliothèque web-vitals peut être utilisée 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'
});

Pour analyser les données de terrain de vos utilisateurs, vous devez les envoyer 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);
});

Toutefois, ces données ne vous disent pas beaucoup plus que CrUX. C'est là qu'intervient la version d'attribution de la bibliothèque Web Vitals.

Aller plus loin avec la création d'attribution de la bibliothèque web-vitals

La compilation d'attribution de la bibliothèque Web Vitals fournit des données supplémentaires que vous pouvez obtenir auprès des utilisateurs sur le terrain pour vous aider à mieux résoudre les 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
});
Affichage des journaux de la console de la bibliothèque Web Vitals La console de cet exemple affiche le nom de la métrique (INP), la valeur INP (56), qui se situe dans les seuils INP (bon), ainsi que les différents éléments d'information affichés dans l'objet d'attribution, y compris les entrées de l'API Long Animation Frames.
Présentation des données de la bibliothèque Web Vitals dans la console.

En plus de l'INP de la page elle-même, la version d'attribution fournit de nombreuses données qui vous aideront à comprendre les raisons des interactions lentes, y compris la partie de l'interaction sur laquelle vous devez vous concentrer. Il peut vous aider à répondre à des questions importantes, par exemple:

  • "L'utilisateur a-t-il interagi avec la page pendant son chargement ?"
  • "Les gestionnaires d'événements de l'interaction ont-ils été exécutés pendant une longue période ?"
  • "Le code du gestionnaire d'événements d'interaction a-t-il été retardé ? Si oui, qu'est-ce qui se passait d'autre sur le thread principal à ce moment-là ?"
  • "L'interaction a-t-elle généré beaucoup de travail de rendu qui a retardé l'affichage du frame suivant ?"

Le tableau suivant présente certaines des données d'attribution de base que vous pouvez obtenir à partir de la bibliothèque. Elles peuvent vous aider à identifier certaines causes générales des interactions lentes 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, par exemple clics, appuis ou saisie au clavier.
inputDelay* Délai de réponse à l'entrée de l'interaction.
processingDuration* Temps écoulé entre le début de l'exécution du premier écouteur d'événement en réponse à l'interaction de l'utilisateur et la fin du traitement de l'écouteur d'événement.
presentationDelay* Délai de présentation de l'interaction, qui se produit à partir de la fin des gestionnaires d'événements jusqu'au moment où le frame suivant est peint.
longAnimationFrameEntries* Entrées de la LoAF associées à l'interaction. Pour en savoir plus, consultez la page suivante.
*Nouveauté de la version 4

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

API Long Animation Frames (LoAF)

Navigateurs pris en charge

  • Chrome: 123.
  • Edge: 123.
  • Firefox: non compatible.
  • Safari: non compatible.

Source

Le débogage des interactions à l'aide de données de terrain est une tâche difficile. Grâce aux données de LoAF, vous pouvez désormais obtenir de meilleurs insights sur les causes des interactions lentes, car LoAF fournit un certain nombre de temps détaillés et d'autres données que vous pouvez utiliser pour identifier les causes exactes, et plus important encore, l'emplacement de la source du problème dans le code de votre site Web.

La version 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 liste quelques éléments clés de données que vous pouvez trouver dans chaque entrée de la liste d'autorisations d'accès:

Clé d'objet d'entrée LoAF Données
duration Durée du frame d'animation long, jusqu'à la fin de la mise en page, mais en excluant la peinture et le compositing.
blockingDuration Durée totale pendant laquelle le navigateur n'a pas pu répondre rapidement en raison de tâches longues. Ce temps de blocage peut inclure des tâches longues exécutées en JavaScript, ainsi que toute tâche de rendu longue ultérieure dans le frame.
firstUIEventTimestamp Code temporel de l'événement 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 Code temporel de début du frame.
renderStart Date de début du rendu du frame. Cela inclut tous les rappels requestAnimationFrame (et les rappels ResizeObserver le cas échéant), mais potentiellement avant le début de tout travail de style/mise en page.
styleAndLayoutStart Lorsque le travail de style/mise en page dans le frame se produit. Peut être utile pour déterminer la durée du travail de style/mise en page en tenant compte des autres codes temporels disponibles.
scripts Tableau d'éléments contenant des informations d'attribution de script contribuant à l'INP de la page.
Visualisation d'un frame d'animation long selon le modèle LoAF.
Diagramme des délais d'un frame d'animation long selon l'API LoAF (moins blockingDuration).

Toutes ces informations peuvent vous en dire beaucoup sur ce qui ralentit une interaction. Toutefois, le tableau scripts que les entrées LoAF affichent devrait vous intéresser particulièrement:

Clé d'objet d'attribution de script Données
invoker L'appelant. Cela peut varier en fonction du type d'appelant décrit dans la ligne suivante. Des valeurs telles que 'IMG#id.onload', 'Window.requestAnimationFrame' ou 'Response.json.then' peuvent être des exemples d'appelants.
invokerType Type de l'appelant. Il peut s'agir de 'user-callback', 'event-listener', 'resolve-promise', 'reject-promise', 'classic-script' ou 'module-script'.
sourceURL URL du script à l'origine du frame d'animation long.
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 fournissent des informations sur le script à l'origine de l'interaction lente et sur la raison pour laquelle il en est à l'origine.

Mesurer et identifier les causes courantes des interactions lentes

Pour vous donner une idée de la façon dont vous pouvez utiliser ces informations, ce guide explique comment utiliser les données LoAF présentées dans la bibliothèque web-vitals pour déterminer certaines causes des interactions lentes.

Durée de traitement longue

La durée de traitement d'une interaction correspond au temps nécessaire pour que les rappels du gestionnaire d'événements enregistrés pour l'interaction s'exécutent jusqu'à la fin, ainsi que tout autre événement pouvant se produire entre eux. La bibliothèque web-vitals affiche 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 principale cause d'une interaction lente est que le code du 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 bien le problème, vous pouvez aller plus loin avec les données 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 LoAF pour identifier la cause exacte d'une interaction avec des valeurs de durée de traitement élevées, y compris:

  • L'élément et son écouteur d'événements enregistré.
  • Fichier de script (et position des caractères dans celui-ci) 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 de chercher à savoir exactement quelle interaction (ou quel gestionnaire d'événements) était responsable des valeurs de durée de traitement élevées. De plus, comme les scripts tiers peuvent souvent enregistrer leurs propres gestionnaires d'événements, vous pouvez déterminer si c'est votre code qui en est à l'origine ou non. Pour le code que vous contrôlez, vous devez envisager d'optimiser les tâches longues.

Retards de saisie importants

Bien que les gestionnaires d'événements de longue durée soient courants, d'autres éléments de l'interaction doivent être pris en compte. Une partie se produit avant la durée de traitement, ce qui s'appelle le délai d'entrée. Il s'agit du temps écoulé entre le moment où l'utilisateur lance l'interaction et celui où les rappels du gestionnaire d'événements commencent à s'exécuter. Cela se produit lorsque le thread principal traite déjà une autre tâche. La version 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 d'entrée élevés, vous devez déterminer ce qui se passait sur la page au moment de l'interaction qui a causé le long délai d'entrée. Cela se résume souvent à savoir si l'interaction s'est produite pendant le chargement de la page ou après.

Est-ce que le problème est survenu lors du chargement de la page ?

Le thread principal est souvent le plus chargé lorsque la page est en cours de chargement. Pendant ce temps, toutes sortes de tâches sont mises en file d'attente et traitées. Si l'utilisateur tente d'interagir avec la page pendant que tout ce travail est en cours, l'interaction peut être retardée. Les pages qui chargent beaucoup de code JavaScript peuvent lancer des compilations et des évaluations de scripts, ainsi que des fonctions qui préparent la page aux interactions utilisateur. Cette tâche peut gêner l'utilisateur s'il interagit au moment de cette activité. Vous pouvez vérifier 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 retards d'entrée élevés et des types d'appelants de 'classic-script' ou 'module-script', vous pouvez affirmer que l'évaluation des scripts sur votre site prend beaucoup de temps et qu'ils bloquent le thread principal suffisamment longtemps pour retarder les interactions. Vous pouvez réduire ce temps de blocage en divisant vos scripts en petits bundles, en différant le chargement du code initialement inutilisé à un moment ultérieur et en auditant votre site pour identifier le code inutilisé que vous pouvez supprimer.

S'agissait-il d'un problème survenu après le chargement de la page ?

Bien que les retards de saisie se produisent souvent pendant le chargement d'une page, ils peuvent tout aussi bien se produire après le chargement d'une page, pour une raison totalement différente. Les causes courantes des retards d'entrée après le chargement de la page peuvent être un code qui s'exécute régulièrement en raison d'un appel setInterval précédent, ou même des rappels d'événements qui ont été mis en file d'attente pour s'exécuter plus tôt et qui sont toujours en cours de traitement.

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 pour résoudre les problèmes liés aux valeurs de durée de traitement élevées, les retards d'entrée élevés dus aux causes mentionnées précédemment vous fourniront des données d'attribution de script détaillées. Cependant, le type d'appelant change en fonction de la nature de la tâche qui a retardé l'interaction:

  • 'user-callback' indique que la tâche bloquante provient de setInterval, setTimeout ou même requestAnimationFrame.
  • 'event-listener' indique que la tâche bloquante provient d'une entrée précédente mise en file d'attente et toujours en cours de traitement.
  • 'resolve-promise' et 'reject-promise' signifient que la tâche bloquante provient d'un travail asynchrone lancé précédemment, et qu'elle a été 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 de script vous indiqueront où commencer à chercher et si le délai de saisie était dû à votre propre code ou à celui d'un script tiers.

Retards de présentation importants

Les délais de présentation sont la dernière étape d'une interaction. Ils commencent lorsque les gestionnaires d'événements de l'interaction se terminent, jusqu'au moment où le frame suivant a été peint. Ils se produisent lorsque le travail effectué dans un gestionnaire d'événements en raison 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 la durée du 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 que vous constatez des délais de présentation élevés pour les interactions contribuant à l'INP de votre site Web, les causes peuvent varier, mais voici quelques éléments à surveiller.

Travail de style et de mise en page coûteux

Les longs délais de présentation peuvent être dus à des recalculs de style et à des travaux de mise en page coûteux qui découlent de plusieurs causes, y compris des sélecteurs CSS complexes et des grandes tailles de DOM. Vous pouvez mesurer la durée de ce travail avec les temps de chargement de l'élément affichés 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 indique pas la durée du travail de style et de mise en page pour un frame, mais il vous indique quand il a commencé. Avec ce code temporel de début, vous pouvez utiliser d'autres données de LoAF pour calculer la durée exacte de ce travail en déterminant l'heure de fin du frame, puis en soustrayant le code temporel de début de la tâche de style et de mise en page.

Rappels requestAnimationFrame de longue durée

Un travail excessif effectué dans un rappel requestAnimationFrame peut être une cause potentielle de longs retards de présentation. 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 des styles et le travail de mise en page.

Ces rappels peuvent prendre un temps considérable si le travail effectué en leur sein est complexe. Si vous pensez que des valeurs de délai de présentation élevées sont dues au travail que vous effectuez avec requestAnimationFrame, vous pouvez utiliser les données LoAF fournies 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 partie importante du délai de présentation est consacrée à un rappel requestAnimationFrame, assurez-vous que le travail que vous effectuez dans ces rappels est limité à des tâches qui entraînent une actualisation réelle de l'interface utilisateur. Toute autre tâche qui ne touche pas au DOM ni ne met à jour les styles retardera inutilement la peinture du prochain frame. Faites donc attention !

Conclusion

Les données sur le terrain sont la meilleure source d'informations à laquelle vous pouvez vous référer pour comprendre quelles interactions sont problématiques pour les utilisateurs réels sur le terrain. En vous appuyant sur des outils de collecte de données sur le terrain tels que la bibliothèque JavaScript Web Vitals (ou un fournisseur de RUM), vous pouvez identifier plus précisément les interactions les plus problématiques, puis les reproduire en laboratoire et les corriger.

Image principale issue de Unsplash, par Federico Respini.