Découvrez les bases de l'utilisation des API Navigation et Resource Timing pour évaluer les performances de chargement sur le terrain.
Publié le 8 octobre 2021
Si vous avez déjà utilisé le débit limité de la connexion dans le panneau "Réseau" des outils pour les développeurs d'un navigateur (ou Lighthouse dans Chrome) pour évaluer les performances de chargement, vous savez à quel point ces outils sont pratiques pour optimiser les performances. Vous pouvez mesurer rapidement l'impact des optimisations des performances avec une vitesse de connexion de référence cohérente et stable. Le seul problème est qu'il s'agit de tests synthétiques, qui fournissent des données expérimentales, et non des données réelles.
Les tests synthétiques ne sont pas nécessairement mauvais, mais ils ne reflètent pas la vitesse de chargement de votre site Web pour les utilisateurs réels. Cela nécessite des données de champ, que vous pouvez collecter à partir des API Navigation Timing et Resource Timing.
API vous permettant d'évaluer les performances de chargement sur le terrain
Navigation Timing et Resource Timing sont deux API similaires qui se chevauchent de manière significative et qui mesurent deux choses distinctes:
- Navigation Timing mesure la vitesse des requêtes pour les documents HTML (c'est-à-dire les requêtes de navigation).
- Resource Timing mesure la vitesse des requêtes pour les ressources dépendantes des documents, telles que les fichiers CSS, JavaScript, images et autres types de ressources.
Ces API exposent leurs données dans un tampon d'entrée de performances, auquel vous pouvez accéder dans le navigateur avec JavaScript. Il existe plusieurs façons d'interroger un tampon de performances, mais une méthode courante consiste à utiliser performance.getEntriesByType
:
// Get Navigation Timing entries:
performance.getEntriesByType('navigation');
// Get Resource Timing entries:
performance.getEntriesByType('resource');
performance.getEntriesByType
accepte une chaîne décrivant le type d'entrées que vous souhaitez récupérer à partir du tampon d'entrées de performances. 'navigation'
et 'resource'
récupèrent les temps de l'API Navigation Timing et de l'API Resource Timing, respectivement.
Ces API peuvent fournir beaucoup d'informations, mais elles sont essentielles pour mesurer les performances de chargement sur le terrain, car vous pouvez recueillir ces données auprès des utilisateurs lorsqu'ils visitent votre site Web.
Durée de vie et délais d'une requête réseau
La collecte et l'analyse des délais de navigation et de ressources ressemblent à de l'archéologie, car vous reconstruisez la vie éphémère d'une requête réseau après coup. Il est parfois utile de visualiser des concepts. Pour les requêtes réseau, les outils pour les développeurs de votre navigateur peuvent vous aider.
La durée de vie d'une requête réseau comporte des phases distinctes, telles que la résolution DNS, l'établissement de la connexion, la négociation TLS et d'autres sources de latence. Ces délais sont représentés par un DOMHighResTimestamp
. Selon votre navigateur, la précision des temps peut être au microseconde près ou arrondie à la milliseconde. Vous devez examiner ces phases en détail et leur relation avec les paramètres Navigation Timing et Resource Timing.
résolution DNS
Lorsqu'un utilisateur accède à une URL, le système de noms de domaine (DNS) est interrogé pour traduire un domaine en adresse IP. Ce processus peut prendre un certain temps, que vous devrez même mesurer sur le terrain. Les paramètres Navigation Timing et Resource Timing exposent deux temps liés au DNS:
domainLookupStart
correspond au début de la recherche DNS.domainLookupEnd
correspond à la fin de la résolution DNS.
Pour calculer la durée totale de la résolution DNS, soustrayez la métrique de début à la métrique de fin:
// Measuring DNS lookup time
const [pageNav] = performance.getEntriesByType('navigation');
const totalLookupTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;
Négociation de connexion
La négociation de connexion, qui correspond à la latence induite lors de la connexion à un serveur Web, est un autre facteur contribuant aux performances de chargement. Si HTTPS est utilisé, ce processus inclura également l'heure de la négociation TLS. La phase de connexion se compose de trois temps:
connectStart
correspond au moment où le navigateur commence à ouvrir une connexion à un serveur Web.secureConnectionStart
indique le moment où le client commence la négociation TLS.connectEnd
correspond au moment où la connexion au serveur Web a été établie.
La mesure de la durée totale de la connexion est semblable à la mesure de la durée totale de la résolution DNS: vous soustractez l'heure de début de l'heure de fin. Toutefois, il existe une propriété secureConnectionStart
supplémentaire qui peut être 0
si HTTPS n'est pas utilisé ou si la connexion est persistante. Si vous souhaitez mesurer le temps de négociation TLS, gardez cela à l'esprit:
// Quantifying total connection time
const [pageNav] = performance.getEntriesByType('navigation');
const connectionTime = pageNav.connectEnd - pageNav.connectStart;
let tlsTime = 0; // <-- Assume 0 to start with
// Was there TLS negotiation?
if (pageNav.secureConnectionStart > 0) {
// Awesome! Calculate it!
tlsTime = pageNav.connectEnd - pageNav.secureConnectionStart;
}
Une fois la recherche DNS et la négociation de la connexion terminées, les délais liés à l'extraction des documents et de leurs ressources dépendantes entrent en jeu.
Requêtes et réponses
Les performances de chargement sont affectées par deux types de facteurs:
- Facteurs externes:latence et bande passante, par exemple. En dehors du choix d'une société d'hébergement et éventuellement d'un CDN, nous n'avons (presque) aucun contrôle sur ces éléments, car les utilisateurs peuvent accéder au Web de n'importe où.
- Facteurs intrinsèques:il s'agit d'éléments tels que les architectures côté serveur et côté client, ainsi que la taille des ressources et notre capacité à les optimiser, qui sont sous notre contrôle.
Ces deux types de facteurs ont un impact sur les performances de chargement. Les délais associés à ces facteurs sont essentiels, car ils décrivent la durée de téléchargement des ressources. Navigation Timing et Resource Timing décrivent les performances de chargement à l'aide des métriques suivantes:
fetchStart
indique le moment où le navigateur commence à extraire une ressource (temps de latence des ressources) ou un document pour une requête de navigation (temps de latence de la navigation). Cette étape précède la requête réelle et correspond au moment où le navigateur vérifie les caches (par exemple, les instances HTTP etCache
).workerStart
indique le moment où une requête commence à être gérée dans le gestionnaire d'événementsfetch
d'un service worker. La valeur sera0
si aucun service worker ne contrôle la page actuelle.requestStart
correspond au moment où le navigateur envoie la requête.responseStart
correspond au moment où le premier octet de la réponse arrive.responseEnd
correspond au moment où le dernier octet de la réponse arrive.
Ces délais vous permettent de mesurer plusieurs aspects des performances de chargement, tels que la recherche dans le cache dans un service worker et le temps de téléchargement:
// Cache seek plus response time of the current document
const [pageNav] = performance.getEntriesByType('navigation');
const fetchTime = pageNav.responseEnd - pageNav.fetchStart;
// Service worker time plus response time
let workerTime = 0;
if (pageNav.workerStart > 0) {
workerTime = pageNav.responseEnd - pageNav.workerStart;
}
Vous pouvez également mesurer d'autres aspects de la latence des requêtes et des réponses:
const [pageNav] = performance.getEntriesByType('navigation');
// Request time only (excluding redirects, DNS, and connection/TLS time)
const requestTime = pageNav.responseStart - pageNav.requestStart;
// Response time only (download)
const responseTime = pageNav.responseEnd - pageNav.responseStart;
// Request + response time
const requestResponseTime = pageNav.responseEnd - pageNav.requestStart;
Autres mesures que vous pouvez effectuer
Les propriétés Navigation Timing et Resource Timing sont utiles pour bien plus que ce que les exemples précédents décrivent. Voici d'autres situations avec des délais pertinents qui peuvent être intéressants à explorer:
- Redirections de page:les redirections sont une source de latence négligée, en particulier les chaînes de redirection. La latence est ajoutée de plusieurs manières, telles que les sauts HTTP vers HTTPS, ainsi que les redirections 302/301 non mises en cache. Les temps
redirectStart
,redirectEnd
etredirectCount
sont utiles pour évaluer la latence de redirection. - Déchargement de documents:dans les pages qui exécutent du code dans un gestionnaire d'événements
unload
, le navigateur doit exécuter ce code avant de pouvoir accéder à la page suivante.unloadEventStart
etunloadEventEnd
mesurent le déchargement des documents. - Traitement des documents:le temps de traitement des documents peut ne pas avoir d'incidence, sauf si votre site Web envoie des charges utiles HTML très volumineuses. Si c'est votre cas, les délais
domInteractive
,domContentLoadedEventStart
,domContentLoadedEventEnd
etdomComplete
peuvent vous intéresser.
Obtenir des temps dans votre code
Tous les exemples présentés jusqu'à présent utilisent performance.getEntriesByType
, mais il existe d'autres façons d'interroger le tampon d'entrée des performances, comme performance.getEntriesByName
et performance.getEntries
. Ces méthodes sont efficaces lorsque seule une analyse légère est nécessaire. Dans d'autres cas, cependant, ils peuvent entraîner une charge de travail excessive du thread principal en itérant sur un grand nombre d'entrées, voire en interrogeant à plusieurs reprises le tampon de performances pour trouver de nouvelles entrées.
L'approche recommandée pour collecter des entrées à partir du tampon d'entrée des performances consiste à utiliser un PerformanceObserver
. PerformanceObserver
écoute les entrées de performances et les fournit lorsqu'elles sont ajoutées au tampon:
// Create the performance observer:
const perfObserver = new PerformanceObserver((observedEntries) => {
// Get all resource entries collected so far:
const entries = observedEntries.getEntries();
// Iterate over entries:
for (let i = 0; i < entries.length; i++) {
// Do the work!
}
});
// Run the observer for Navigation Timing entries:
perfObserver.observe({
type: 'navigation',
buffered: true
});
// Run the observer for Resource Timing entries:
perfObserver.observe({
type: 'resource',
buffered: true
});
Cette méthode de collecte des codes temporels peut sembler gênante par rapport à l'accès direct au tampon d'entrée des performances, mais il est préférable de relier le thread principal à une tâche qui ne remplit pas un objectif critique ni visible par l'utilisateur.
Appeler chez soi
Une fois que vous avez collecté tous les délais dont vous avez besoin, vous pouvez les envoyer à un terminal pour une analyse plus approfondie. Pour ce faire, vous pouvez utiliser navigator.sendBeacon
ou fetch
avec l'option keepalive
définie. Les deux méthodes envoient une requête à un point de terminaison spécifié de manière non bloquante. La requête est mise en file d'attente de manière à survivre à la session de page actuelle si nécessaire:
// Check for navigator.sendBeacon support:
if ('sendBeacon' in navigator) {
// Caution: If you have lots of performance entries, don't
// do this. This is an example for illustrative purposes.
const data = JSON.stringify(performance.getEntries());
// Send the data!
navigator.sendBeacon('/analytics', data);
}
Dans cet exemple, la chaîne JSON arrive dans une charge utile POST
que vous pouvez décoder, traiter et stocker dans un backend d'application si nécessaire.
Conclusion
Une fois les métriques collectées, il vous appartient de déterminer comment analyser ces données sur le terrain. Lorsque vous analysez des données sur le terrain, vous devez respecter certaines règles générales pour vous assurer de tirer des conclusions pertinentes:
- Évitez les moyennes, car elles ne sont pas représentatives de l'expérience d'un utilisateur donné et peuvent être faussées par des valeurs aberrantes.
- Utilisez les centiles. Dans les ensembles de données de métriques de performances basées sur le temps, plus la valeur est faible, mieux c'est. Autrement dit, lorsque vous priorisez les percentiles faibles, vous ne vous intéressez qu'aux expériences les plus rapides.
- Privilégiez la longue traîne de valeurs. Lorsque vous donnez la priorité aux expériences correspondant au 75e centile ou plus, vous privilégiez les expériences les plus lentes.
Ce guide n'a pas vocation à être une ressource exhaustive sur la navigation ou le chronométrage des ressources, mais un point de départ. Voici quelques ressources supplémentaires qui pourraient vous être utiles:
- Spécification de la synchronisation de navigation
- Spécification de temps des ressources.
- ResourceTiming en pratique
- API Navigation Timing (MDN)
- API Resource Timing (MDN)
Grâce à ces API et aux données qu'elles fournissent, vous serez mieux à même de comprendre comment les performances de chargement sont perçues par les utilisateurs réels. Vous pourrez ainsi diagnostiquer et résoudre plus facilement les problèmes de performances de chargement sur le terrain.