Présentation de l'API Performance

Publié: 2022-08-10

L'API Performance mesure la réactivité de votre application Web en direct sur des appareils utilisateur réels et des connexions réseau. Il peut aider à identifier les goulots d'étranglement dans votre code côté client et côté serveur avec :

  • temporisation de l'utilisateur : mesure personnalisée des performances de la fonction JavaScript côté client
  • temps de peinture : métriques de rendu du navigateur
  • synchronisation des ressources : performances de chargement des ressources et appels Ajax
  • synchronisation de la navigation : métriques de chargement de page, y compris les redirections, les recherches DNS, la préparation DOM, etc.

L'API résout plusieurs problèmes associés à l'évaluation des performances typique :

  1. Les développeurs testent souvent les applications sur des PC haut de gamme connectés à un réseau rapide. DevTools peut émuler des appareils plus lents, mais il ne mettra pas toujours en évidence les problèmes du monde réel lorsque la majorité des clients utilisent un mobile de deux ans connecté au WiFi de l'aéroport.
  2. Les options tierces telles que Google Analytics sont souvent bloquées, ce qui entraîne des résultats et des hypothèses faussés. Vous pouvez également rencontrer des implications en matière de confidentialité dans certains pays.
  3. L'API Performance peut évaluer avec précision diverses métriques mieux que des méthodes telles que Date() .

Vous souhaitez en savoir plus sur l'utilisation de l'API Performance ? Commencez ici... Cliquez pour tweeter
Les sections suivantes décrivent les différentes manières d'utiliser l'API Performance. Une certaine connaissance de JavaScript et des métriques de chargement de page est recommandée.

Disponibilité de l'API de performances

La plupart des navigateurs modernes prennent en charge l'API Performance, y compris IE10 et IE11 (même IE9 a une prise en charge limitée). Vous pouvez détecter la présence de l'API en utilisant :

 if ('performance' in window) { // use Performance API }

Il n'est pas possible de remplir complètement l'API, alors méfiez-vous des navigateurs manquants. Si 90 % de vos utilisateurs naviguent avec plaisir avec Internet Explorer 8, vous ne mesureriez que 10 % des clients avec des applications plus performantes.

L'API peut être utilisée dans les Web Workers, qui permettent d'exécuter des calculs complexes dans un thread d'arrière-plan sans interrompre les opérations du navigateur.

La plupart des méthodes d'API peuvent être utilisées dans Node.js côté serveur avec le module standard perf_hooks :

 // Node.js performance import { performance } from 'node:perf_hooks'; // or in Common JS: const { performance } = require('node:perf_hooks'); console.log( performance.now() );

Deno fournit l'API Performance standard :

 // Deno performance console.log( performance.now() );

Vous devrez exécuter des scripts avec la permission --allow-hrtime pour activer la mesure du temps haute résolution :

 deno run --allow-hrtime index.js

Les performances côté serveur sont généralement plus faciles à évaluer et à gérer car elles dépendent de la charge, des processeurs, de la RAM, des disques durs et des limites du service cloud. Les mises à niveau matérielles ou les options de gestion des processus telles que PM2, le clustering et Kubernetes peuvent être plus efficaces que la refactorisation du code.

Les sections suivantes se concentrent sur les performances côté client pour cette raison.

Mesure de performance personnalisée

L'API Performance peut être utilisée pour chronométrer la vitesse d'exécution des fonctions de votre application. Vous avez peut-être utilisé ou rencontré des fonctions de chronométrage en utilisant Date() :

 const timeStart = new Date(); runMyCode(); const timeTaken = new Date() - timeStart; console.log(`runMyCode() executed in ${ timeTaken }ms`);

L'API Performance offre deux avantages principaux :

  1. Meilleure précision : Date() mesure à la milliseconde près, mais l'API Performance peut mesurer des fractions de milliseconde (selon le navigateur).
  2. Meilleure fiabilité : l'utilisateur ou le système d'exploitation peut modifier l'heure du système, de sorte que les métriques basées sur Date() ne seront pas toujours exactes. Cela signifie que vos fonctions peuvent sembler particulièrement lentes lorsque les horloges avancent !

L'équivalent Date() est performance.now() qui renvoie un horodatage haute résolution qui est mis à zéro lorsque le processus responsable de la création du document démarre (la page est chargée) :

 const timeStart = performance.now(); runMyCode(); const timeTaken = performance.now() - timeStart; console.log(`runMyCode() executed in ${ timeTaken }ms`);

Une propriété performance.timeOrigin non standard peut également renvoyer un horodatage à partir du 1er janvier 1970, bien que cela ne soit pas disponible dans IE et Deno.

performance.now() devient peu pratique lorsque vous effectuez plus de quelques mesures. L'API Performance fournit un tampon dans lequel vous pouvez enregistrer un événement pour une analyse ultérieure en transmettant un nom d'étiquette à performance.mark() :

 performance.mark('start:app'); performance.mark('start:init'); init(); // run initialization functions performance.mark('end:init'); performance.mark('start:funcX'); funcX(); // run another function performance.mark('end:funcX'); performance.mark('end:app');

Un tableau de tous les objets de marque dans le tampon de performance peut être extrait en utilisant :

 const mark = performance.getEntriesByType('mark');

Exemple de résultat :

 [ { detail: null duration: 0 entryType: "mark" name: "start:app" startTime: 1000 }, { detail: null duration: 0 entryType: "mark" name: "start:init" startTime: 1001 }, { detail: null duration: 0 entryType: "mark" name: "end:init" startTime: 1100 }, ... ]

La méthode performance.measure() calcule le temps entre deux marques et le stocke également dans le tampon Performance. Vous passez un nouveau nom de mesure, le nom de la marque de début (ou null pour mesurer à partir du chargement de la page) et le nom de la marque de fin (ou null pour mesurer à l'heure actuelle) :

 performance.measure('init', 'start:init', 'end:init');

Un objet PerformanceMeasure est ajouté au tampon avec la durée calculée. Pour obtenir cette valeur, vous pouvez soit demander un tableau de toutes les mesures :

 const measure = performance.getEntriesByType('measure');

ou demander une mesure par son nom :

 performance.getEntriesByName('init');

Exemple de résultat :

 [ { detail: null duration: 99 entryType: "measure" name: "init" startTime: 1001 } ]

Utilisation du tampon de performances

En plus des marques et des mesures, le tampon de performances est utilisé pour enregistrer automatiquement le temps de navigation, le temps des ressources et le temps de peinture (dont nous parlerons plus tard). Vous pouvez obtenir un tableau de toutes les entrées du tampon :

 performance.getEntries();

Par défaut, la plupart des navigateurs fournissent une mémoire tampon qui stocke jusqu'à 150 métriques de ressources. Cela devrait suffire pour la plupart des évaluations, mais vous pouvez augmenter ou diminuer la limite de tampon si nécessaire :

 // record 500 metrics performance.setResourceTimingBufferSize(500);

Les repères peuvent être effacés par leur nom ou vous pouvez spécifier une valeur vide pour effacer tous les repères :

 performance.clearMarks('start:init');

De même, les mesures peuvent être effacées par leur nom ou une valeur vide pour tout effacer :

 performance.clearMeasures();

Surveillance des mises à jour des tampons de performances

Un PerformanceObserver peut surveiller les modifications apportées au tampon de performances et exécuter une fonction lorsque des événements spécifiques se produisent. La syntaxe vous sera familière si vous avez utilisé MutationObserver pour répondre aux mises à jour DOM ou IntersectionObserver pour détecter le défilement des éléments dans la fenêtre d'affichage.

Vous devez définir une fonction d'observation avec deux paramètres :

  1. un tableau d'entrées d'observateur qui ont été détectées, et
  2. l'objet observateur. Si nécessaire, sa méthode disconnect() peut être appelée pour arrêter l'observateur.
 function performanceCallback(list, observer) { list.getEntries().forEach(entry => { console.log(`name : ${ entry.name }`); console.log(`type : ${ entry.type }`); console.log(`start : ${ entry.startTime }`); console.log(`duration: ${ entry.duration }`); }); }

La fonction est transmise à un nouvel objet PerformanceObserver. Sa méthode observe() reçoit un tableau d'entryTypes de tampon de performance à observer :

 let observer = new PerformanceObserver( performanceCallback ); observer.observe({ entryTypes: ['mark', 'measure'] });

Dans cet exemple, l'ajout d'un nouveau repère ou d'une nouvelle mesure exécute la fonction performanceCallback() . Bien qu'il n'enregistre que les messages ici, il peut être utilisé pour déclencher un téléchargement de données ou effectuer d'autres calculs.

Mesurer les performances de la peinture

L'API Paint Timing n'est disponible qu'en JavaScript côté client et enregistre automatiquement deux métriques importantes pour Core Web Vitals :

  1. first-paint : le navigateur a commencé à dessiner la page.
  2. first-contentful-paint : le navigateur a peint le premier élément significatif du contenu DOM, tel qu'un titre ou une image.

Ceux-ci peuvent être extraits du tampon Performance vers un tableau :

 const paintTimes = performance.getEntriesByType('paint');

Méfiez-vous de l'exécuter avant que la page ne soit complètement chargée ; les valeurs ne seront pas prêtes. Attendez l'événement window.load ou utilisez un PerformanceObserver pour surveiller les types d'entrée de paint .

Aux prises avec des temps d'arrêt et des problèmes WordPress ? Kinsta est la solution d'hébergement conçue pour vous faire gagner du temps ! Découvrez nos fonctionnalités

Exemple de résultat :

 [ { "name": "first-paint", "entryType": "paint", "startTime": 812, "duration": 0 }, { "name": "first-contentful-paint", "entryType": "paint", "startTime": 856, "duration": 0 } ]

Une première peinture lente est souvent causée par un CSS ou JavaScript bloquant le rendu. L'écart avec la première peinture de contenu peut être important si le navigateur doit télécharger une grande image ou rendre des éléments complexes.

Mesure de la performance des ressources

Les minutages réseau pour les ressources telles que les images, les feuilles de style et les fichiers JavaScript sont automatiquement enregistrés dans le tampon de performances. Bien que vous ne puissiez pas faire grand-chose pour résoudre les problèmes de vitesse du réseau (autres que la réduction de la taille des fichiers), cela peut aider à mettre en évidence les problèmes liés aux actifs plus volumineux, aux réponses Ajax lentes ou aux scripts tiers peu performants.

Un tableau de métriques PerformanceResourceTiming peut être extrait du tampon en utilisant :

 const resources = performance.getEntriesByType('resource');

Vous pouvez également récupérer les métriques d'un élément en transmettant son URL complète :

 const resource = performance.getEntriesByName('https://test.com/script.js');

Exemple de résultat :

 [ { connectEnd: 195, connectStart: 195, decodedBodySize: 0, domainLookupEnd: 195, domainLookupStart: 195, duration: 2, encodedBodySize: 0, entryType: "resource", fetchStart: 195, initiatorType: "script", name: "https://test.com/script.js", nextHopProtocol: "h3", redirectEnd: 0, redirectStart: 0, requestStart: 195, responseEnd: 197, responseStart: 197, secureConnectionStart: 195, serverTiming: [], startTime: 195, transferSize: 0, workerStart: 195 } ]

Les propriétés suivantes peuvent être examinées :

  • nom : URL de la ressource
  • type d'entrée : "ressource"
  • initiatorType : comment la ressource a été initiée, par exemple "script" ou "lien"
  • serverTiming : un tableau d'objets PerformanceServerTiming transmis par le serveur dans l'en-tête HTTP Server-Timing (votre application côté serveur peut envoyer des métriques au client pour une analyse plus approfondie)
  • startTime : horodatage du début de la récupération
  • nextHopProtocol : protocole réseau utilisé
  • workerStart : Horodatage avant le démarrage d'un Progressive Web App Service Worker (0 si la requête n'est pas interceptée par un Service Worker)
  • redirectStart : horodatage du démarrage d'une redirection
  • redirectEnd : horodatage après le dernier octet de la dernière réponse de redirection
  • fetchStart : horodatage avant la récupération de la ressource
  • domainLookupStart : Horodatage avant une recherche DNS
  • domainLookupEnd : Horodatage après la recherche DNS
  • connectStart : horodatage avant l'établissement d'une connexion au serveur
  • connectEnd : horodatage après l'établissement d'une connexion au serveur
  • secureConnectionStart : Horodatage avant la poignée de main SSL
  • requestStart : horodatage avant que le navigateur ne demande la ressource
  • responseStart : Horodatage lorsque le navigateur reçoit le premier octet de données
  • responseEnd : horodatage après réception du dernier octet ou fermeture de la connexion
  • duration : La différence entre startTime et responseEnd
  • transferSize : la taille de la ressource en octets, y compris l'en-tête et le corps compressé
  • encodedBodySize : Le corps de la ressource en octets avant décompression
  • decodedBodySize : Le corps de la ressource en octets après décompression

Cet exemple de script récupère toutes les requêtes Ajax initiées par l'API Fetch et renvoie la taille et la durée totales du transfert :

 const fetchAll = performance.getEntriesByType('resource') .filter( r => r.initiatorType === 'fetch') .reduce( (sum, current) => { return { transferSize: sum.transferSize += current.transferSize, duration: sum.duration += current.duration } }, { transferSize: 0, duration: 0 } );

Mesure des performances de navigation

Les minutages réseau pour le déchargement de la page précédente et le chargement de la page actuelle sont automatiquement enregistrés dans la mémoire tampon de performances sous la forme d'un seul objet PerformanceNavigationTiming .

Extrayez-le dans un tableau en utilisant :

 const pageTime = performance.getEntriesByType('navigation');

…ou en passant l'URL de la page à .getEntriesByName() :

 const pageTiming = performance.getEntriesByName(window.location);

Les métriques sont identiques à celles des ressources, mais incluent également des valeurs spécifiques à la page :

  • entryType : par exemple "navigation"
  • type : soit "navigate", "reload", "back_forward" ou "prerender"
  • redirectCount : Le nombre de redirections
  • unloadEventStart : Horodatage avant l'événement de déchargement du document précédent
  • unloadEventEnd : horodatage après l'événement de déchargement du document précédent
  • domInteractive : Horodatage lorsque le navigateur a analysé le HTML et construit le DOM
  • domContentLoadedEventStart : horodatage avant le déclenchement de l'événement DOMContentLoaded du document
  • domContentLoadedEventEnd : horodatage après la fin de l'événement DOMContentLoaded du document
  • domComplete : horodatage après la fin de la construction du DOM et des événements DOMContentLoaded
  • loadEventStart : horodatage avant le déclenchement de l'événement de chargement de la page
  • loadEventEnd : Horodatage après l'événement de chargement de la page et tous les actifs sont disponibles

Les problèmes typiques incluent :

  • Un long délai entre unloadEventEnd et domInteractive . Cela pourrait indiquer une réponse lente du serveur.
  • Un long délai entre domContentLoadedEventStart et domComplete . Cela peut indiquer que les scripts de démarrage de la page sont trop lents.
  • Un long délai entre domComplete et loadEventEnd . Cela peut indiquer que la page contient trop d'éléments ou que plusieurs d'entre eux prennent trop de temps à se charger.

Enregistrement et analyse des performances

L'API Performance vous permet de rassembler des données d'utilisation réelles et de les télécharger sur un serveur pour une analyse plus approfondie. Vous pouvez utiliser un service tiers tel que Google Analytics pour stocker les données, mais il existe un risque que le script tiers soit bloqué ou présente de nouveaux problèmes de performances. Votre propre solution peut être personnalisée selon vos besoins pour garantir que la surveillance n'affecte pas d'autres fonctionnalités.

Méfiez-vous des situations dans lesquelles les statistiques ne peuvent pas être déterminées, peut-être parce que les utilisateurs utilisent d'anciens navigateurs, bloquent JavaScript ou sont derrière un proxy d'entreprise. Comprendre quelles données manquent peut être plus fructueux que de faire des hypothèses basées sur des informations incomplètes.

Idéalement, vos scripts d'analyse n'auront pas d'impact négatif sur les performances en exécutant des calculs complexes ou en téléchargeant de grandes quantités de données. Envisagez d'utiliser des travailleurs Web et de minimiser l'utilisation d'appels localStorage synchrones. Il est toujours possible de traiter ultérieurement les données brutes par lots.

Enfin, méfiez-vous des valeurs aberrantes telles que les appareils et les connexions très rapides ou très lents qui affectent négativement les statistiques. Par exemple, si neuf utilisateurs chargent une page en deux secondes mais que le dixième subit un téléchargement de 60 secondes, la latence moyenne est de près de 8 secondes. Une métrique plus réaliste est le chiffre médian (2 secondes) ou le 90e centile (9 utilisateurs sur 10 connaissent un temps de chargement de 2 secondes ou moins).

Sommaire

Les performances Web restent un facteur critique pour les développeurs. Les utilisateurs s'attendent à ce que les sites et les applications soient réactifs sur la plupart des appareils. L'optimisation des moteurs de recherche peut également être affectée car les sites plus lents sont rétrogradés dans Google.
Tout ce que vous devez savoir pour démarrer avec l'API Performance est ici Cliquez pour tweeter
Il existe de nombreux outils de surveillance des performances, mais la plupart évaluent les vitesses d'exécution côté serveur ou utilisent un nombre limité de clients capables pour juger du rendu du navigateur. L'API Performance fournit un moyen de rassembler des métriques d'utilisateurs réels qu'il ne serait pas possible de calculer autrement.