API Long Animation Frames

L'API Long Animation Frames (LoAF, prononcé Lo-Af) est une mise à jour de l'API Long Tasks pour vous aider à mieux comprendre les mises à jour lentes de l'interface utilisateur (UI). Cela peut être utile pour identifier les images d'animation lentes susceptibles d'affecter la métrique Interaction to Next Paint (INP), qui mesure la réactivité, ou d'autres à-coups dans l'interface utilisateur qui affectent la fluidité.

État de l'API

Navigateurs pris en charge

  • 123
  • x
  • x
  • x

Après une phase d'évaluation de Chrome 116 à Chrome 122, l'API LoAF est expédiée depuis Chrome 123.

API Long Tasks

Navigateurs pris en charge

  • 58
  • 79
  • x
  • x

Source

L'API Long Animation Frames est une alternative à l'API Long Tasks, qui est disponible dans Chrome depuis un certain temps (depuis Chrome 58). Comme son nom l'indique, l'API Long Task vous permet de surveiller les longues tâches, qui occupent le thread principal pendant 50 millisecondes ou plus. Les tâches longues peuvent être surveillées à l'aide de l'interface PerformanceLongTaskTiming, avec un PeformanceObserver:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'longtask', buffered: true });

Les tâches longues sont susceptibles de causer des problèmes de réactivité. Si un utilisateur tente d'interagir avec une page (par exemple, cliquer sur un bouton ou ouvrir un menu), mais que le thread principal traite déjà une longue tâche, l'interaction de l'utilisateur est retardée dans l'attente de cette tâche.

Pour améliorer la réactivité, il est souvent conseillé de segmenter les tâches longues. Si chaque longue tâche est plutôt décomposée en une série de plusieurs tâches plus petites, cela peut permettre d’exécuter des tâches plus importantes entre elles afin d’éviter des retards importants dans la réponse aux interactions.

Ainsi, lorsque vous essayez d'améliorer la réactivité, la première chose à faire est souvent d'exécuter une trace des performances et d'examiner les tâches longues. Pour cela, vous pouvez utiliser un outil d'audit basé sur des ateliers comme Lighthouse (qui propose un audit Éviter les longues tâches du thread principal) ou consulter les tâches longues dans les outils pour les développeurs Chrome.

Les tests basés sur des laboratoires sont souvent un mauvais point de départ pour identifier les problèmes de réactivité, car ces outils peuvent ne pas inclure les interactions. Lorsqu'ils le font, ils ne constituent qu'un petit sous-ensemble d'interactions probables. Idéalement, vous devriez mesurer les causes de lenteur des interactions sur le terrain.

Inconvénients de l'API Long Tasks

Mesurer les longues tâches sur le terrain à l'aide d'un observateur de performances n'est que très utile. En réalité, elle ne donne pas autant d'informations que le fait qu'une longue tâche s'est produite et combien de temps elle a pris.

Les outils RUM (Real User Monitoring) s'en servent souvent pour déterminer les tendances du nombre ou de la durée des tâches longues, ou pour identifier les pages sur lesquelles elles se produisent. Mais sans les détails sous-jacents de la cause de la tâche longue, cette utilisation n'est que limitée. L'API Long Tasks ne dispose que d'un modèle d'attribution de base, qui ne vous indique au mieux que le conteneur dans lequel la tâche longue s'est produite (le document de premier niveau ou un <iframe>), mais pas le script ou la fonction qui l'a appelée, comme illustré dans une entrée type:

{
  "name": "unknown",
  "entryType": "longtask",
  "startTime": 31.799999997019768,
  "duration": 136,
  "attribution": [
    {
      "name": "unknown",
      "entryType": "taskattribution",
      "startTime": 0,
      "duration": 0,
      "containerType": "window",
      "containerSrc": "",
      "containerId": "",
      "containerName": ""
    }
  ]
}

L'API Long Tasks est également une vue incomplète, car elle peut également exclure certaines tâches importantes. Certaines mises à jour, telles que l'affichage, sont effectuées dans des tâches distinctes qui, idéalement, devraient être incluses avec l'exécution précédente qui a entraîné cette mise à jour afin de mesurer avec précision le "travail total" de cette interaction. Pour en savoir plus sur les limites de l'utilisation de tâches, consultez la section Où les tâches longues ne sont pas à la hauteur ? de la vidéo d'explication.

Dernier problème : la mesure des tâches longues ne génère des rapports que sur les tâches individuelles qui dépassent la limite de 50 millisecondes. Un frame d'animation peut être composé de plusieurs tâches inférieures à cette limite de 50 millisecondes, mais qui, collectivement, empêchent le navigateur de s'afficher.

API Long Animation Frames

Navigateurs pris en charge

  • 123
  • x
  • x
  • x

L'API Long Animation Frames (LoAF) est une nouvelle API qui vise à combler certaines des lacunes de l'API Long Tasks pour permettre aux développeurs d'obtenir des insights plus exploitables afin de résoudre les problèmes de réactivité et d'améliorer l'INP.

Une bonne réactivité signifie qu'une page réagit rapidement aux interactions effectuées avec elle. Cela implique de pouvoir afficher rapidement toutes les mises à jour dont l'utilisateur a besoin et d'éviter de les bloquer. Pour INP, il est recommandé de répondre en 200 millisecondes au maximum, mais pour les autres informations (par exemple, les animations), même celles qui peuvent être trop longues.

L'API Long Animation Frames est une approche alternative pour mesurer le travail de blocage. Plutôt que de mesurer les tâches individuelles, l'API Long Animation Frames, comme son nom l'indique, mesure les longues images d'animation. Une image d'animation longue se produit lorsqu'une mise à jour du rendu est retardée au-delà de 50 millisecondes (comme le seuil pour l'API Long Tasks).

Les images d'animation longues peuvent être observées de la même manière que les tâches longues avec un PerformanceObserver, mais en examinant le type long-animation-frame à la place:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Vous pouvez également interroger les images d'animation longues précédentes à partir de Performance Timeline (Chronologie des performances) comme suit:

const loafs = performance.getEntriesByType('long-animation-frame');

Toutefois, il existe une maxBufferSize pour les entrées de performance après quoi les nouvelles entrées sont supprimées. L'approche PerformanceObserver est donc recommandée. La taille de la mémoire tampon long-animation-frame est définie sur 200, comme pour long-tasks.

Avantages de regarder des cadres plutôt que des tâches

Le principal avantage de cette méthode est qu'une animation de longue durée peut être constituée d'un nombre indéfini de tâches ayant engendré une longue image d'animation. Cela concerne le dernier point mentionné précédemment, où la somme des nombreuses tâches plus petites qui bloquent l'affichage avant une image d'animation peut ne pas être révélée par l'API Long Tasks.

Un autre avantage de cette vue alternative sur les tâches longues est la possibilité de fournir des répartitions temporelles de l'ensemble du frame. Plutôt que d'inclure simplement un startTime et un duration, comme l'API Long Tasks, LoAF inclut une répartition beaucoup plus détaillée des différentes parties de la durée d'affichage, y compris:

  • startTime: heure de début de la longue image d'animation par rapport à l'heure de début de la navigation.
  • duration: durée de la longue image de l'animation (sans compter l'heure de présentation).
  • renderStart: heure de début du cycle de rendu, qui inclut les rappels requestAnimationFrame, le calcul du style et de la mise en page, l'observateur de redimensionnement et les rappels d'observateur d'intersection.
  • styleAndLayoutStart: début de la période consacrée aux calculs de style et de mise en page.
  • firstUIEventTimestamp: temps du premier événement d'interface utilisateur (souris/clavier, etc.) à gérer pendant le frame.
  • blockingDuration: durée, en millisecondes, pendant laquelle l'image de l'animation était bloquée.

Ces codes temporels permettent de diviser la longue image d'animation en codes temporels:

Durée Calcul
Heure de début startTime
Heure de fin startTime + duration
Durée du travail renderStart ? renderStart - startTime : duration
Durée du rendu renderStart ? (startTime + duration) - renderStart: 0
Rendu: durée de pré-mise en page styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
Rendu: style et durée de mise en page styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

Pour en savoir plus sur ces durées individuelles, consultez la vidéo explicative qui explique précisément quelle activité contribue à une longue image d'animation.

Meilleure attribution

Le type d'entrée long-animation-frame inclut de meilleures données d'attribution pour chaque script ayant contribué à une longue image d'animation.

Comme pour l'API Long Tasks, ces données seront fournies dans un tableau d'entrées d'attribution, chacune présentant les informations suivantes:

  • name et EntryType renverront tous deux script.
  • invoker significatif, indiquant la manière dont le script a été appelé (par exemple, 'IMG#id.onload', 'Window.requestAnimationFrame' ou 'Response.json.then').
  • Le invokerType du point d'entrée du script :
    • user-callback: rappel connu enregistré à partir d'une API de plate-forme Web (par exemple, setTimeout ou requestAnimationFrame).
    • event-listener: écouteur d'un événement de plate-forme (par exemple, click, load, keyup).
    • resolve-promise: gestionnaire d'une promesse de plate-forme (par exemple, fetch()). Notez que dans le cas des promesses, tous les gestionnaires des mêmes promesses sont mélangés sous la forme d'un "script".).
    • reject-promise: conformément à resolve-promise, mais pour le refus.
    • classic-script: évaluation du script (par exemple, <script> ou import())
    • module-script: identique à classic-script, mais pour les scripts de module.
  • Données temporelles distinctes pour ce script :
    • startTime: heure à laquelle la fonction d'entrée a été appelée.
    • duration: durée comprise entre startTime et le moment où le traitement de la file d'attente de microtâches suivante est terminé.
    • executionStart: temps après la compilation.
    • forcedStyleAndLayoutDuration: temps total consacré au traitement d'une mise en page ou d'un style forcés dans cette fonction (voir la section sur le thrashing).
    • pauseDuration: temps total passé en "mise en pause" des opérations synchrones (alerte, requête XHR synchrone).
  • Détails de la source du script :
    • sourceURL: nom de la ressource de script lorsqu'il est disponible (ou vide s'il est introuvable).
    • sourceFunctionName: nom de la fonction du script lorsqu'il est disponible (ou vide dans le cas contraire).
    • sourceCharPosition: position du caractère de script lorsqu'elle est disponible (ou "-1" si elle est introuvable).
  • windowAttribution: conteneur (document de premier niveau ou <iframe>) dans lequel l'image de l'animation longue s'est affichée.
  • window: référence à la fenêtre de même origine.

Lorsqu'elles sont fournies, les entrées sources permettent aux développeurs de savoir exactement comment chaque script de la longue image d'animation a été appelé, jusqu'à la position du caractère dans le script appelant. Cela donne l'emplacement exact dans la ressource JavaScript à l'origine de la longue image d'animation.

Exemple d'entrée de performance long-animation-frame

Voici un exemple complet d'entrée de performance long-animation-frame contenant un seul script:

{
  "blockingDuration": 0,
  "duration": 60,
  "entryType": "long-animation-frame",
  "firstUIEventTimestamp": 11801.099999999627,
  "name": "long-animation-frame",
  "renderStart": 11858.800000000745,
  "scripts": [
    {
      "duration": 45,
      "entryType": "script",
      "executionStart": 11803.199999999255,
      "forcedStyleAndLayoutDuration": 0,
      "invoker": "DOMWindow.onclick",
      "invokerType": "event-listener",
      "name": "script",
      "pauseDuration": 0,
      "sourceURL": "https://web.dev/js/index-ffde4443.js",
      "sourceFunctionName": "myClickHandler",
      "sourceCharPosition": 17796,
      "startTime": 11803.199999999255,
      "window": [Window object],
      "windowAttribution": "self"
    }
  ],
  "startTime": 11802.400000000373,
  "styleAndLayoutStart": 11858.800000000745
}

Comme on peut le voir, cela fournit une quantité sans précédent de données qui permettent aux sites Web de comprendre la cause des mises à jour lentes de l'affichage.

Activer l'API Long Animation Frames

L'API Long Animation Frames est activée par défaut à partir de Chrome 123.

Utiliser l'API Long Animation Frames sur le terrain

Bien qu'utiles pour détecter et reproduire des problèmes, des outils tels que Lighthouse sont des outils d'atelier qui peuvent passer à côté d'aspects importants de l'expérience utilisateur que seules les données réelles peuvent fournir. L'API Long Animation Frames peut être utilisée sur le terrain pour collecter des données contextuelles importantes sur les interactions utilisateur que l'API Long Tasks n'aurait pas pu utiliser. Cela peut vous aider à mettre en évidence et à reproduire des problèmes d'interactivité que vous n'auriez peut-être pas découverts autrement.

Vous trouverez ci-dessous quelques suggestions de stratégies. Toutefois, l'équipe Chrome aimerait connaître son avis sur cette API et sur la façon dont les développeurs et les fournisseurs de RUM utiliseraient les informations fournies par l'API.

Prise en charge de l'API de détection de caractéristiques pour les frames d'animation longs

Vous pouvez utiliser le code suivant pour vérifier si l'API est compatible:

if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
  // Monitor LoAFs
}

Dans ce cas, vous pouvez utiliser l'alternative suivante, lorsque les frames d'animation longs ne sont pas encore pris en charge par défaut et sont dans cet état de transition:

if ('PerformanceLongAnimationFrameTiming' in window) {
  // Monitor LoAFs
}

Signaler des données d'animation longues à un point de terminaison d'analyse

Comme indiqué, l'entrée de performances LoAF comprend des informations précieuses. Une stratégie consisterait à surveiller tous les LoAF et à baliser ceux qui dépassent un certain seuil vers un point de terminaison d'analyse pour une analyse ultérieure:

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.duration > REPORTING_THRESHOLD_MS) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Étant donné que les longues entrées de frame d'animation peuvent être assez volumineuses, les développeurs doivent décider quelles données de l'entrée doivent être envoyées à l'analyse. Par exemple, les heures de résumé de l'entrée et éventuellement les noms des scripts, ou tout autre ensemble minimal d'autres données contextuelles jugées nécessaires.

Observer les images d'animation les plus longues

Les sites peuvent souhaiter collecter des données sur la ou les images d'animation les plus longues afin de réduire le volume de données devant être balisées. Ainsi, quel que soit le nombre de frames d'animation longs sur une page, seules les données pour la pire, les cinq images ou le nombre de frames d'animation longs absolument nécessaires sont balisés.

MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];

const observer = new PerformanceObserver(list => {
  longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
    (a, b) => b.blockingDuration - a.blockingDuration
  ).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Au moment opportun (idéalement lors de l'événement visibilitychange) redirigez les utilisateurs vers les données analytiques. Pour les tests en local, vous pouvez régulièrement utiliser console.table:

console.table(longestBlockingLoAFs);

Créer un lien vers la plus longue interaction INP

En complément de l'observation des pires loAF, le ou les cadres LoAF correspondant à l'entrée INP peuvent être utilisés comme données d'attribution pour donner plus de détails sur la façon d'améliorer l'INP.

Il n'existe actuellement aucune API directe permettant d'associer une entrée INP à sa ou ses entrées LoAF associées, bien que cela soit possible dans le code en comparant les heures de début et de fin de chacune (voir cet exemple de script).

Signaler des images d'animation longues avec des interactions

Une autre approche qui nécessite moins de code consisterait à toujours envoyer les entrées LoAF les plus grandes (ou les X plus grandes) lorsqu'une interaction s'est produite pendant la trame (ce qui peut être détecté par la présence d'une valeur firstUIEventTimestamp). Dans la plupart des cas, cela inclut l'interaction INP pour une visite donnée. Dans de rares cas, si ce n'est pas le cas, cela indique quand même de longues interactions qu'il est important de corriger, car il peut s'agir d'interactions INP pour d'autres utilisateurs.

Le code suivant enregistre toutes les entrées LoAF de plus de 150 millisecondes au cours desquelles une interaction s'est produite pendant la trame. La valeur 150 est choisie ici, car elle est légèrement inférieure au seuil "bon" INP de 200 millisecondes. Vous pouvez choisir une valeur supérieure ou inférieure selon vos besoins.

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
    for (const entry of list.getEntries()) {
      if (entry.duration > REPORTING_THRESHOLD_MS &&
        entry.firstUIEventTimestamp > 0
      ) {
        // Example here logs to console, but could also report back to analytics
        console.log(entry);
      }
    }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Identifier des modèles communs dans les longues images d'animation

Une autre stratégie consiste à examiner les scripts courants qui apparaissent le plus dans les longues entrées de frame d'animation. Les données peuvent être transmises au niveau du script et/ou de la position des personnages pour identifier les récidivistes.

Cela peut s'avérer particulièrement utile pour les plates-formes personnalisables, où les thèmes ou plug-ins à l'origine de problèmes de performances peuvent être plus facilement identifiés sur un certain nombre de sites.

Le temps d'exécution des scripts courants (ou origines tierces) pour les longues images d'animation peut être résumé et rapporté afin d'identifier les contributeurs courants à de longues images d'animation sur un site ou un ensemble de sites. Par exemple, pour examiner les URL:

const observer = new PerformanceObserver(list => {
  const allScripts = list.getEntries().flatMap(entry => entry.scripts);
  const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
  const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
      allScripts.filter(script => script.sourceURL === sourceURL)
  ]));
  const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
    sourceURL,
    count: scripts.length,
    totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
  }));
  processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
  // Example here logs to console, but could also report back to analytics
  console.table(processedScripts);
});

observer.observe({type: 'long-animation-frame', buffered: true});

Voici un exemple de résultat:

(index) sourceURL count totalDuration
0 'https://example.consent.com/consent.js' 1 840
1 'https://example.com/js/analytics.js' 7 628
2 'https://example.chatapp.com/web-chat.js' 1 5

Utiliser l'API Long Animation Frames dans les outils

L'API peut également permettre d'autres outils de développement pour le débogage local. Certains outils tels que Lighthouse et Chrome DevTools ont pu collecter une grande partie de ces données à l'aide de détails de traçage de niveau inférieur, mais cette API de niveau supérieur pourrait permettre à d'autres outils d'accéder à ces données.

Afficher les données sur les frames d'animation longs dans les outils de développement

Vous pouvez afficher de longues images d'animation dans les outils de développement à l'aide de l'API performance.measure(), qui s'affichent ensuite dans le canal de temps utilisateur des outils de développement dans les traces de performances pour indiquer où concentrer vos efforts pour améliorer les performances:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Si cette API s'avère utile à long terme, elle sera probablement intégrée aux outils de développement lui-même. Toutefois, l'extrait de code précédent permet de l'afficher dans les outils de développement.

Utiliser des données sur les frames d'animation longs dans d'autres outils pour les développeurs

L'extension Web Vitals a montré l'intérêt de consigner des informations de débogage dans le récapitulatif pour diagnostiquer les problèmes de performances. Maintenant que l'API est lancée, des outils comme celui-ci peuvent afficher des données plus facilement pour que les développeurs sachent sur quoi concentrer leurs efforts. Nous prévoyons également de l'ajouter à la bibliothèque JavaScript Web Vitals dans la version 4.

Utiliser des données sur les frames d'animation longues dans les outils de test automatisés

De même, les outils de test automatisés, éventuellement dans les pipelines CI/CD, peuvent fournir des informations sur les problèmes de performances potentiels en mesurant de longues images d'animation lors de l'exécution de divers suites de tests.

Questions fréquentes

Voici quelques-unes des questions fréquentes sur cette API:

Pourquoi ne pas simplement étendre ou itérer l'API Long Tasks ?

Il s'agit d'une approche alternative permettant de signaler une mesure similaire, mais différente, des problèmes potentiels de réactivité. Il est important de s'assurer que les sites qui s'appuient sur l'API Long Tasks existante continuent de fonctionner pour éviter d'interrompre les cas d'utilisation existants.

Bien que l'API Long Tasks puisse bénéficier de certaines fonctionnalités de LoAF (comme un meilleur modèle d'attribution), nous pensons que se concentrer sur les frames plutôt que sur les tâches offre de nombreux avantages qui en font une API fondamentalement différente de l'API Long Tasks existante.

Cela remplacera-t-il l'API Long Tasks ?

Bien que nous soyons convaincus que l'API Long Animation Frames soit une API plus efficace et plus complète pour mesurer les tâches longues, il n'est pour l'instant pas prévu d'abandonner cette API.

Commentaires demandés

Vous pouvez envoyer vos commentaires dans la liste des problèmes GitHub ou signaler les bugs dans l'implémentation de l'API dans Chrome dans l'outil de suivi des problèmes de Chrome.

Conclusion

L'API Long Animation Frames est une nouvelle API très intéressante qui présente de nombreux avantages potentiels par rapport à l'ancienne API Long Tasks.

Il s'avère être un outil clé pour résoudre les problèmes de réactivité mesurés par l'INP. L'INP est une métrique difficile à optimiser, et cette API est l'un des moyens utilisés par l'équipe Chrome pour aider les développeurs à identifier et à résoudre les problèmes plus facilement.

Le champ d'application de l'API Long Animation Frames s'étend au-delà de INP et peut aider à identifier d'autres causes de lenteur des mises à jour qui peuvent affecter la fluidité globale de l'expérience utilisateur d'un site Web.

Remerciements

Vignette par Henry Be sur Unsplash.