Un examen approfondi des API de performance Web, des mesures de temps traditionnelles aux indicateurs modernes centrés sur l'utilisateur comme les Core Web Vitals, et comment les connecter pour une vue holistique des performances.
Au-delà de l'horloge: connecter les API de performance Web à l'expérience utilisateur réelle
Dans l'économie numérique, la vitesse n'est pas seulement une fonctionnalité ; c'est le fondement de l'expérience utilisateur. Un site web lent peut entraîner la frustration des utilisateurs, des taux de rebond plus élevés et un impact direct sur les revenus. Pendant des années, les développeurs se sont appuyés sur des mesures de temps telles que window.onload
pour évaluer les performances. Mais un temps de chargement rapide équivaut-il vraiment à un utilisateur satisfait ? La réponse est souvent non.
Une page peut terminer de charger toutes ses ressources techniques en moins d'une seconde, tout en étant lente et inutilisable pour une personne réelle qui essaie d'interagir avec elle. Cette déconnexion met en évidence une évolution essentielle du développement web : le passage de la mesure des temps techniques à la quantification de l'expérience humaine. La performance web moderne est un récit à deux perspectives : les données granulaires et de bas niveau fournies par les API de performance web et les indicateurs de haut niveau, centrés sur l'utilisateur, tels que les Core Web Vitals de Google.
Ce guide complet comblera cet écart. Nous explorerons la puissante suite d'API de performance web qui agissent comme nos outils de diagnostic. Ensuite, nous examinerons en profondeur les indicateurs modernes de l'expérience utilisateur qui nous indiquent comment les performances sont *ressenties*. Plus important encore, nous relierons les points, en vous montrant comment utiliser les données de temps de bas niveau pour diagnostiquer et corriger les causes profondes d'une mauvaise expérience utilisateur pour votre public mondial.
Les bases : comprendre les API de performance web
Les API de performance web sont un ensemble d'interfaces de navigateur normalisées qui donnent aux développeurs un accès à des données de temps très détaillées et précises relatives à la navigation et au rendu d'une page web. Elles sont le fondement de la mesure des performances, nous permettant de dépasser les simples chronomètres et de comprendre la danse complexe des requêtes réseau, de l'analyse et du rendu.
API Navigation Timing : le parcours de la page
L'API Navigation Timing fournit une ventilation détaillée du temps nécessaire pour charger le document principal. Elle capture les jalons à partir du moment où un utilisateur lance la navigation (comme en cliquant sur un lien) jusqu'au moment où la page est entièrement chargée. Il s'agit de notre première et plus fondamentale vue sur le processus de chargement de la page.
Vous pouvez accéder à ces données avec un simple appel JavaScript :
const navigationEntry = performance.getEntriesByType('navigation')[0];
console.log(navigationEntry.toJSON());
Cela renvoie un objet débordant d'horodatages. Certaines propriétés clés comprennent :
- fetchStart : lorsque le navigateur commence à récupérer le document.
- responseStart : lorsque le navigateur reçoit le premier octet de la réponse du serveur. Le temps entre
fetchStart
etresponseStart
est souvent appelé Time to First Byte (TTFB). - domContentLoadedEventEnd : lorsque le document HTML initial a été complètement chargé et analysé, sans attendre que les feuilles de style, les images et les sous-images aient fini de se charger.
- loadEventEnd : lorsque toutes les ressources de la page (y compris les images, CSS, etc.) ont été entièrement chargées.
Pendant longtemps, loadEventEnd
était la référence absolue. Cependant, sa limitation est sévère : elle ne dit rien sur le moment où l'utilisateur *voit* un contenu significatif ou quand il peut *interagir* avec la page. Il s'agit d'un jalon technique, pas humain.
API Resource Timing : déconstruction des composants
Une page web est rarement un seul fichier. Il s'agit d'un assemblage de HTML, CSS, JavaScript, d'images, de polices et d'appels d'API. L'API Resource Timing vous permet d'inspecter le temps de réseau pour chacune de ces ressources individuelles.
Ceci est incroyablement puissant pour identifier les goulots d'étranglement. Une image principale volumineuse et non optimisée provenant d'un réseau de diffusion de contenu (CDN) situé sur un autre continent ralentit-elle le rendu initial ? Un script d'analyse tiers bloque-t-il le thread principal ? Resource Timing vous aide à répondre à ces questions.
Vous pouvez obtenir une liste de toutes les ressources comme ceci :
const resourceEntries = performance.getEntriesByType('resource');
resourceEntries.forEach(resource => {
if (resource.duration > 200) { // Trouver les ressources qui ont pris plus de 200 ms
console.log(`Ressource lente : ${resource.name}, Durée : ${resource.duration}ms`);
}
});
Les propriétés clés comprennent name
(l'URL de la ressource), initiatorType
(ce qui a causé le chargement de la ressource, par exemple, 'img', 'script') et duration
(le temps total nécessaire pour la récupérer).
API User Timing : mesure de la logique de votre application
Parfois, le goulot d'étranglement des performances n'est pas dans le chargement des actifs, mais dans le code côté client lui-même. Combien de temps faut-il à votre application monopage (SPA) pour afficher un composant complexe après réception des données d'une API ? L'API User Timing vous permet de créer des mesures personnalisées spécifiques à l'application.
Elle fonctionne avec deux méthodes principales :
- performance.mark(name) : crée un horodatage nommé dans la mémoire tampon de performance.
- performance.measure(name, startMark, endMark) : calcule la durée entre deux marques et crée une mesure nommée.
Exemple : Mesure du temps de rendu d'un composant de liste de produits.
// Lorsque vous commencez à extraire des données
performance.mark('product-list-fetch-start');
fetch('/api/products')
.then(response => response.json())
.then(data => {
// Après l'extraction, avant le rendu
performance.mark('product-list-render-start');
renderProductList(data);
// Immédiatement après la fin du rendu
performance.mark('product-list-render-end');
// Créer une mesure
performance.measure(
'Temps de rendu de la liste de produits',
'product-list-render-start',
'product-list-render-end'
);
});
Cela vous donne un contrôle précis pour mesurer les parties de votre application qui sont les plus essentielles au flux de travail de l'utilisateur.
PerformanceObserver : l'approche moderne et efficace
L'interrogation constante de performance.getEntriesByType()
est inefficace. L'API PerformanceObserver
fournit une bien meilleure façon d'écouter les entrées de performance. Vous vous abonnez à des types d'entrée spécifiques, et le navigateur avertit votre fonction de rappel de manière asynchrone à mesure qu'elles sont enregistrées. C'est la façon recommandée de collecter les données de performance sans ajouter de surcharge à votre application.
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`Type d'entrée : ${entry.entryType}, Nom : ${entry.name}`);
}
});
observer.observe({ entryTypes: ['resource', 'navigation', 'mark', 'measure'] });
Cet observateur est la clé pour collecter non seulement les indicateurs traditionnels ci-dessus, mais aussi les indicateurs modernes, centrés sur l'utilisateur, dont nous discuterons ensuite.
Le passage à la centralisation sur l'utilisateur : Core Web Vitals
Savoir qu'une page a été chargée en 2 secondes est utile, mais cela ne répond pas aux questions essentielles : L'utilisateur a-t-il regardé un écran vide pendant ces 2 secondes ? A-t-il pu interagir avec la page, ou était-elle figée ? Le contenu a-t-il sauté de façon inattendue pendant qu'il essayait de lire ?
Pour répondre à cela, Google a introduit les Core Web Vitals (CWV), un ensemble d'indicateurs conçus pour mesurer l'expérience utilisateur réelle d'une page sur trois dimensions clés : le chargement, l'interactivité et la stabilité visuelle.
Largest Contentful Paint (LCP) : mesure du chargement perçu
LCP mesure le temps de rendu de l'image ou du bloc de texte le plus grand visible dans la fenêtre d'affichage. C'est un excellent indicateur du moment où l'utilisateur sent que le contenu principal de la page a été chargé. Il répond directement à la question de l'utilisateur : « Cette page est-elle déjà utile ? »
- Bon : en dessous de 2,5 secondes
- Nécessite des améliorations : entre 2,5 s et 4,0 s
- Mauvais : au-dessus de 4,0 secondes
Contrairement à loadEventEnd
, LCP se concentre sur ce que l'utilisateur voit en premier, ce qui en fait un reflet beaucoup plus précis de la vitesse de chargement perçue.
Interaction to Next Paint (INP) : mesure de la réactivité
INP est le successeur de First Input Delay (FID) et est devenu un Core Web Vital officiel en mars 2024. Alors que FID ne mesurait que le délai de la *première* interaction, INP mesure la latence de *toutes* les interactions de l'utilisateur (clics, tapotements, frappes de touches) tout au long du cycle de vie de la page. Il signale l'interaction la plus longue, identifiant ainsi efficacement la réactivité la plus mauvaise qu'un utilisateur puisse rencontrer.
INP mesure le temps total entre l'entrée de l'utilisateur et la prochaine image affichée, reflétant la rétroaction visuelle. Il répond à la question de l'utilisateur : « Lorsque je clique sur ce bouton, la page répond-elle rapidement ? »
- Bon : en dessous de 200 millisecondes
- Nécessite des améliorations : entre 200 ms et 500 ms
- Mauvais : au-dessus de 500 ms
Une INP élevée est généralement causée par un thread principal occupé, où les tâches JavaScript de longue durée empêchent le navigateur de répondre à l'entrée de l'utilisateur.
Cumulative Layout Shift (CLS) : mesure de la stabilité visuelle
CLS mesure la stabilité visuelle d'une page. Il quantifie la quantité de contenu qui se déplace de façon inattendue sur l'écran pendant le processus de chargement. Un score CLS élevé est une source courante de frustration pour l'utilisateur, par exemple lorsque vous essayez de cliquer sur un bouton, mais qu'une publicité se charge au-dessus de lui, le poussant vers le bas et vous faisant cliquer sur la publicité à la place.
CLS répond à la question de l'utilisateur : « Puis-je utiliser cette page sans que les éléments sautent partout ? »
- Bon : en dessous de 0,1
- Nécessite des améliorations : entre 0,1 et 0,25
- Mauvais : au-dessus de 0,25
Les causes courantes d'une CLS élevée incluent les images ou les iframes sans dimensions, les polices web qui se chargent tardivement ou le contenu injecté dynamiquement dans la page sans réserver d'espace pour celui-ci.
Combler le fossé : utiliser les API pour diagnostiquer une mauvaise expérience utilisateur
C'est là que tout se rejoint. Les Core Web Vitals nous disent *ce que* l'utilisateur a vécu (par exemple, un LCP lent). Les API de performance web nous disent *pourquoi* cela s'est produit. En les combinant, nous passons de la simple observation des performances au diagnostic et à la correction actifs de celles-ci.
Diagnostiquer un LCP lent
Imaginez que votre outil Real User Monitoring (RUM) signale un LCP médiocre de 4,5 secondes pour les utilisateurs d'une région spécifique. Comment le corrigez-vous ? Vous devez décomposer le temps LCP en ses parties constitutives.
- Time to First Byte (TTFB) : le serveur est-il lent à répondre ? Utilisez l'API Navigation Timing. La durée
responseStart - requestStart
vous donne un TTFB précis. Si celui-ci est élevé, le problème se situe sur votre backend, la configuration de votre serveur ou votre base de données, pas sur le frontend. - Délai de chargement des ressources et temps : l'élément LCP lui-même est-il lent à charger ? Tout d'abord, identifiez l'élément LCP (par exemple, une image principale). Vous pouvez utiliser un
PerformanceObserver
pour'largest-contentful-paint'
afin d'obtenir l'élément lui-même. Ensuite, utilisez l'API Resource Timing pour trouver l'entrée pour l'URL de cet élément. Analysez sa chronologie : y a-t-il eu un longconnectStart
àconnectEnd
(réseau lent) ? LeresponseStart
àresponseEnd
était-il long (une taille de fichier énorme) ? SonfetchStart
a-t-il été retardé parce qu'il était bloqué par d'autres ressources bloquant le rendu comme CSS ou JavaScript ? - Délai de rendu de l'élément : c'est le temps entre la fin du chargement de la ressource et le moment où elle est réellement peinte à l'écran. Cela peut être causé par le fait que le thread principal est occupé par d'autres tâches, comme l'exécution d'un grand ensemble JavaScript.
En utilisant Navigation et Resource Timing, vous pouvez déterminer si un LCP lent est dû à un serveur lent, à un script bloquant le rendu ou à une image massive non optimisée.
Enquêter sur une INP médiocre
Vos utilisateurs se plaignent que le fait de cliquer sur le bouton « Ajouter au panier » est lent. Votre indicateur INP se situe dans la plage « Mauvais ». Il s'agit presque toujours d'un problème de thread principal.
- Identifier les tâches longues : l'API Long Tasks est votre principal outil ici. Il signale toute tâche sur le thread principal qui prend plus de 50 ms, car tout ce qui est plus long risque d'entraîner un retard perceptible pour l'utilisateur. Configurez un
PerformanceObserver
pour écouter les entrées'longtask'
. - Corréler avec les actions de l'utilisateur : une tâche longue n'est un problème que si elle se produit lorsque l'utilisateur essaie d'interagir. Vous pouvez corréler le
startTime
d'un événement INP (observé viaPerformanceObserver
sur le type'event'
) avec les temps de toutes les tâches longues qui se sont produites à peu près au même moment. Cela vous indique exactement quelle fonction JavaScript a bloqué l'interaction de l'utilisateur. - Mesurer les gestionnaires spécifiques : utilisez l'API User Timing pour obtenir encore plus de granularité. Enveloppez vos gestionnaires d'événements critiques (comme le gestionnaire « click » pour « Ajouter au panier ») avec
performance.mark()
etperformance.measure()
. Cela vous indiquera précisément combien de temps votre propre code prend à s'exécuter et si c'est la source de la tâche longue.
S'attaquer à la CLS élevée
Les utilisateurs signalent que le texte saute pendant qu'ils lisent un article sur leurs appareils mobiles. Votre score CLS est de 0,3.
- Observer les décalages de disposition : utilisez un
PerformanceObserver
pour écouter les entrées'layout-shift'
. Chaque entrée aura unevalue
(sa contribution au score CLS) et une liste desources
, qui sont les éléments DOM qui ont bougé. Cela vous indique *ce qui* a bougé. - Trouver la ressource coupable : La prochaine question est *pourquoi* elle a bougé. Une raison courante est qu'une ressource se charge tardivement et pousse les autres contenus vers le bas. Vous pouvez corréler le
startTime
d'une entréelayout-shift
avec le tempsresponseEnd
des entrées de l'API Resource Timing. Si un décalage de disposition se produit juste après qu'un script publicitaire ou une grande image a terminé de se charger, vous avez probablement trouvé votre coupable. - Solutions proactives : La correction consiste souvent à fournir des dimensions pour les images et les publicités (
<img width="1000" height="600">
) ou à réserver de l'espace sur la page pour le contenu dynamique avant qu'il ne soit chargé. Resource Timing vous aide à identifier les ressources pour lesquelles vous devez être proactif.
Mise en œuvre pratique : construire un système de surveillance mondial
Comprendre ces API est une chose ; les déployer pour surveiller l'expérience de votre base d'utilisateurs mondiale est l'étape suivante. C'est le domaine du Real User Monitoring (RUM).
Tout assembler avec PerformanceObserver
Vous pouvez créer un seul script puissant pour recueillir toutes ces données essentielles. L'objectif est de collecter les indicateurs et leur contexte sans impacter la performance que vous essayez de mesurer.
Voici un extrait conceptuel d'une configuration d'observateur robuste :
const collectedMetrics = {};
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
collectedMetrics.lcp = entry.startTime;
} else if (entry.entryType === 'layout-shift') {
collectedMetrics.cls = (collectedMetrics.cls || 0) + entry.value;
} else if (entry.entryType === 'event') {
// Ceci est une vue simplifiée du calcul de l'INP
const duration = entry.duration;
if (duration > (collectedMetrics.inp || 0)) {
collectedMetrics.inp = duration;
}
}
// ... et ainsi de suite pour d'autres types d'entrée comme 'longtask'
}
});
observer.observe({ entryTypes: ['largest-contentful-paint', 'layout-shift', 'event', 'longtask'] });
Envoyer des données de manière fiable
Une fois que vous avez collecté vos données, vous devez les envoyer à un backend d'analyse pour le stockage et l'analyse. Il est essentiel de le faire sans retarder le déchargement des pages ni perdre des données d'utilisateurs qui ferment rapidement leurs onglets.
L'API navigator.sendBeacon()
est parfaite pour cela. Elle fournit un moyen asynchrone et fiable d'envoyer une petite quantité de données à un serveur, même si la page est en cours de déchargement. Elle ne s'attend pas à une réponse, ce qui la rend légère et non bloquante.
window.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const payload = JSON.stringify(collectedMetrics);
navigator.sendBeacon('/api/performance-analytics', payload);
}
});
L'importance d'une vue globale
Les outils de test en laboratoire comme Lighthouse sont précieux, mais ils fonctionnent dans un environnement contrôlé. Les données RUM collectées à partir de ces API vous indiquent la vérité fondamentale de ce que vivent vos utilisateurs dans différents pays, conditions de réseau et appareils.
Lors de l'analyse de vos données, segmentez-les toujours. Vous pourriez découvrir que :
- Votre LCP est excellent pour les utilisateurs d'Amérique du Nord, mais médiocre pour les utilisateurs d'Australie, car votre serveur d'images principal est basé aux États-Unis.
- Votre INP est élevé sur les appareils Android de milieu de gamme, qui sont populaires sur les marchés émergents, car votre JavaScript est trop gourmand en CPU pour eux.
- Votre CLS n'est un problème que sur des tailles d'écran spécifiques où une requête média CSS provoque un redimensionnement incorrect d'une publicité.
Ce niveau d'informations segmentées vous permet de prioriser les optimisations qui auront l'impact le plus important sur votre base d'utilisateurs réelle, où qu'ils soient.
Conclusion : de la mesure à la maîtrise
Le monde de la performance web a mûri. Nous sommes passés de simples temps techniques à une compréhension sophistiquée de l'expérience perçue par l'utilisateur. Le parcours comporte trois étapes clés :
- Mesurer l'expérience : Utilisez
PerformanceObserver
pour collecter les Core Web Vitals (LCP, INP, CLS). Cela vous dit *ce qui* se passe et *ce que l'utilisateur ressent*. - Diagnostiquer la cause : Utilisez les API de temps fondamentales (Navigation, Resource, User, Long Tasks) pour creuser plus profondément. Cela vous dit *pourquoi* l'expérience est médiocre.
- Agir avec précision : Utilisez les données combinées pour effectuer des optimisations éclairées et ciblées qui s'attaquent à la cause profonde du problème pour des segments d'utilisateurs spécifiques.
En maîtrisant à la fois les indicateurs utilisateur de haut niveau et les API de diagnostic de bas niveau, vous pouvez construire une stratégie de performance holistique. Vous arrêtez de deviner et commencez à concevoir une expérience web qui n'est pas seulement rapide techniquement, mais qui est rapide, réactive et agréable pour chaque utilisateur, sur chaque appareil, partout dans le monde.