Identifier les causes d'un mauvais INP

Une mauvaise réactivité aux interactions donne l'impression que la page est lente, voire complètement défectueuse. L'INP (Interaction to Next Paint) est une métrique basée sur l'API Event Timing. Elle mesure la pire interaction (moins quelques anomalies) pour évaluer la réactivité de la page.

Il peut être difficile d'identifier les causes principales d'une page Web qui ne répond pas, d'autant plus que cela dépend des interactions de l'utilisateur et des conditions environnementales, telles que les capacités de l'appareil et les conditions du réseau. Il est donc encore plus difficile de diagnostiquer ce problème que dans un scénario plus reproductible et prévisible, comme le chargement de page. Les données de laboratoire ne reproduisent qu'un petit sous-ensemble de scénarios utilisateur réels. Il est donc essentiel de mesurer les causes d'un INP lent sur le terrain.

L'API Event Timing permet de diviser la durée de l'INP en trois sous-parties : le délai d'entrée (traitement déjà en cours lorsque l'interaction se produit), la durée de traitement (retards dus directement à l'interaction) et le délai de présentation (retards dus au rendu du frame suivant après l'interaction). Cela permet d'identifier si le problème se situe dans un autre code, dans le code JavaScript de l'interaction ou dans le traitement des mises à jour de rendu par le navigateur, plutôt que de passer directement au code de l'interaction.

Il est possible de recueillir des insights supplémentaires sur le JavaScript, le recalcul de style et le travail de mise en page qui retardent une interaction. Une trace de performances complète à l'aide de l'API JS Self-Profiling est une solution lourde susceptible de causer des problèmes de performances. L'API Long Animation Frames est une API légère qui peut être utilisée sur le terrain pour identifier le code JavaScript lent et distinguer les coûts de recalcul de style et de mise en page lors des interactions INP :

  • Style et mise en page forcés au niveau du script (forcedStyleDuration et forcedLayoutDuration) : signale les recalculs de style synchrones (comme l'appel de getComputedStyle() après des mutations DOM) et les mises en page/reflux forcés (comme la lecture de getBoundingClientRect() ou offsetWidth après des mutations) dans un script en cours d'exécution pendant le délai d'entrée ou la durée de traitement.
  • Style et mise en page de l'affichage au niveau de l'image (styleDuration et layoutDuration) : indique le temps passé à recalculer les styles par rapport à la mise à jour de la mise en page lors de la mise à jour de l'affichage de l'image, ce qui permet d'expliquer un délai de présentation élevé.

Il est essentiel de distinguer le recalcul du style de la mise en page, car ils ont des causes principales et des solutions différentes : une durée de style élevée indique une correspondance de sélecteur CSS coûteuse ou une invalidation de style étendue (corrigée en simplifiant les sélecteurs ou en réduisant la portée des règles), tandis qu'une durée de mise en page élevée indique des calculs géométriques coûteux ou un thrashing de mise en page synchrone (corrigé en regroupant les lectures DOM avant les écritures, en simplifiant la structure DOM ou en utilisant le confinement CSS).

Mise en œuvre

Le calcul de l'INP à partir de l'API Event Timing brute est complexe et comporte plusieurs nuances. Nous vous conseillons d'utiliser un outil ou une bibliothèque RUM pour collecter ces données.

web-vitals est une bibliothèque Open Source de Google qui calcule les métriques Core Web Vitals, y compris l'INP, et inclut également des sous-parties et des données de frame d'animation long pour l'interaction INP.

Obtenir des attributions pour les interactions INP à l'aide de la bibliothèque web-vitals

La bibliothèque web-vitals est une petite bibliothèque utilisée pour mesurer les Core Web Vitals et d'autres métriques de performances. La fonction onINP() permet d'identifier l'interaction la plus lente et inclut des informations sur les sous-parties de l'INP, les scripts exécutés pendant l'interaction, ainsi que les durées de style et de mise en page à l'aide de l'API Long Animation Frames.

// Use the attribution build to get Long Animation Frame data
// alongside the INP metric value.
import { onINP } from 'web-vitals/attribution';

onINP((metric) => {
  const { attribution } = metric;
  const longestScript = attribution.longestScript;

  // Sum rendering-phase style and layout durations across all LoAF entries
  // intersecting the INP interaction (primarily explains presentationDelay).
  let renderStyleDuration = 0;
  let renderLayoutDuration = 0;
  for (const entry of attribution.longAnimationFrameEntries) {
    renderStyleDuration += entry.styleDuration ?? 0;
    renderLayoutDuration += entry.layoutDuration ?? 0;
  }

  // Beacon script, style, and layout attribution for the INP interaction,
  // so you can identify the root cause in production.
  navigator.sendBeacon(
    '/analytics',
    JSON.stringify({
      name: 'INP',
      value: metric.value,
      // These fields give the INP subparts:
      inputDelay: attribution.inputDelay,
      presentationDelay: attribution.presentationDelay,
      processingDuration: attribution.processingDuration,
      interactionTarget: attribution.interactionTarget,
      // These fields identify which script function was responsible
      // for the longest processing during the INP interaction.
      invokerType: longestScript?.entry?.invokerType,
      sourceURL: longestScript?.entry?.sourceURL,
      sourceFunctionName: longestScript?.entry?.sourceFunctionName,
      sourceCharPosition: longestScript?.entry?.sourceCharPosition,
      // subpart indicates which phase (input delay, processing, or
      // presentation delay) the longest script overlapped with most.
      subpart: longestScript?.subpart,
      intersectingDuration: longestScript?.intersectingDuration,
      // Synchronous style and layout work forced inside the longest script
      // (with forcedStyleAndLayoutDuration as a combined fallback):
      forcedStyleDuration: longestScript?.entry?.forcedStyleDuration,
      forcedLayoutDuration: longestScript?.entry?.forcedLayoutDuration,
      forcedStyleAndLayoutDuration: longestScript?.entry?.forcedStyleAndLayoutDuration,
      // Rendering-phase style and layout work across the interaction's frames
      // (with totalStyleAndLayoutDuration as a combined fallback):
      renderStyleDuration,
      renderLayoutDuration,
      totalStyleAndLayoutDuration: attribution.totalStyleAndLayoutDuration
    })
  );
});

Bonnes pratiques

  • À FAIRE : Utilisez d'abord les sous-parties de l'INP pour identifier si le délai est dû au code JavaScript déjà en cours d'exécution (délai d'entrée), au code JavaScript des gestionnaires d'événements pour l'interaction (durée de traitement) ou au rendu ultérieur (délai de présentation).
  • ESSAYEZ d'identifier le JavaScript bloquant le plus important pour obtenir des détails supplémentaires, en particulier pour le délai d'entrée et la durée de traitement.
  • À FAIRE : Distinguez le recalcul de style (styleDuration, forcedStyleDuration) de la mise en page (layoutDuration, forcedLayoutDuration) lorsque vous diagnostiquez une durée de traitement élevée ou un délai de présentation : des temps de style élevés indiquent des sélecteurs CSS coûteux ou des invalidations étendues, tandis que des temps de mise en page élevés indiquent des goulots d'étranglement de la géométrie DOM ou un thrashing de mise en page synchrone.
  • À FAIRE : Privilégiez l'API Long Animation Frames pour fournir ces informations supplémentaires plutôt que des alternatives comme l'API JS Self-Profiling, qui entraîne une surcharge d'exécution plus importante.
  • À FAIRE : Utilisez la bibliothèque web-vitals si aucune autre solution RUM n'est en place. Elle peut identifier l'interaction INP et inclut des informations sur les sous-parties et les scripts exécutés pendant l'interaction (à l'aide de l'API Long Animation Frames).
  • À FAIRE : Renvoyez les informations requises à un service d'analyse plutôt que de simplement les consigner localement.

Compatibilité des navigateurs et stratégies de remplacement

L'API Event Timing est disponible dans la plupart des navigateurs récents et est nécessaire pour calculer l'INP et ses sous-parties. Pour les navigateurs qui ne sont pas compatibles avec cette API, l'INP ne peut pas être mesuré.

L'API Long Animation Frames fournit des détails supplémentaires facultatifs. Vous pouvez l'utiliser sans problème sans solution de secours. Dans la plupart des cas, les opportunités d'amélioration des performances qu'il identifie s'appliquent également aux autres navigateurs.

Si votre cible Baseline est compatible avec l'API Long Animation Frames (long-animation-frames), mais pas avec les propriétés de durée de style et de mise en page distinctes (styleDuration et layoutDuration sur PerformanceLongAnimationFrameTiming, ou forcedStyleDuration et forcedLayoutDuration sur PerformanceScriptTiming), utilisez les codes temporels combinés de style et de mise en page :

  • Utilisez script.forcedStyleAndLayoutDuration sur PerformanceScriptTiming pour le style forcé synchrone et le travail de mise en page dans les scripts.
  • Utilisez metric.attribution.totalStyleAndLayoutDuration dans web-vitals/attribution (ou calculez (entry.paintTime || entry.startTime + entry.duration) - entry.styleAndLayoutStart sur chaque entrée PerformanceLongAnimationFrameTiming) pour combiner le style et la mise en page.