Métriques personnalisées

Les métriques centrées sur l'utilisateur présentent de nombreux avantages, que vous pouvez mesurer de manière universelle sur n'importe quel site Web. Ces métriques vous permettent d'effectuer les opérations suivantes:

  • Comprendre l'expérience des utilisateurs réels sur le Web dans son ensemble.
  • Comparez votre site à celui d'un concurrent.
  • Effectuez le suivi des données utiles et exploitables dans vos outils d'analyse sans avoir à écrire de code personnalisé.

Les métriques universelles constituent un bon point de référence. Toutefois, dans de nombreux cas, vous devez mesurer plus que ces métriques afin de capturer l'expérience complète sur votre site en particulier.

Les métriques personnalisées vous permettent d'évaluer des aspects de l'expérience utilisateur qui peuvent ne s'appliquer qu'à votre site. En voici quelques exemples:

  • Le temps nécessaire pour qu'une application monopage (SPA) passe d'une "page" à une autre.
  • Temps nécessaire pour qu'une page affiche les données extraites d'une base de données pour les utilisateurs connectés.
  • Le temps nécessaire à l'hydratation d'une application rendue côté serveur (SSR)
  • Taux de succès de cache pour les ressources chargées par les visiteurs connus.
  • Latence des événements de clic ou de clavier dans un jeu.

API pour mesurer des métriques personnalisées

Par le passé, les développeurs Web n'avaient pas beaucoup d'API de bas niveau pour mesurer les performances. Ils ont donc dû recourir au piratage pour déterminer si un site était performant.

Par exemple, il est possible de déterminer si le thread principal est bloqué en raison de tâches JavaScript de longue durée en exécutant une boucle requestAnimationFrame et en calculant le delta entre chaque frame. Si le delta est nettement plus long que la fréquence d'images de l'écran, vous pouvez le signaler comme une tâche longue. Ces techniques ne sont toutefois pas recommandées, car elles affectent les performances elles-mêmes (en déchargeant la batterie, par exemple).

Pour mesurer efficacement les performances, vous devez d'abord vous assurer que vos techniques de mesure des performances n'entraînent pas elles-mêmes de problèmes de performances. Par conséquent, pour toutes les métriques personnalisées que vous mesurez sur votre site, il est préférable d'utiliser l'une des API suivantes, si possible.

API Performance Observer

Navigateurs pris en charge

  • 52
  • 79
  • 57
  • 11

Source

L'API Performance Observer est le mécanisme qui collecte et affiche les données de toutes les autres API de performances abordées sur cette page. Il est essentiel de le comprendre pour obtenir des données de qualité.

Vous pouvez utiliser PerformanceObserver pour vous abonner de manière passive aux événements liés aux performances. Cela permet aux rappels d'API de se déclencher pendant les périodes d'inactivité, ce qui signifie qu'ils n'affectent généralement pas les performances de la page.

Pour créer un PerformanceObserver, transmettez-lui un rappel à exécuter chaque fois que de nouvelles entrées de performances sont envoyées. Ensuite, vous indiquez à l'observateur les types d'entrées à écouter à l'aide de la méthode observe():

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });

  po.observe({type: 'some-entry-type'});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Les sections suivantes répertorient les différents types d'entrées disponibles pour l'observation. Toutefois, dans les navigateurs les plus récents, vous pouvez inspecter les types d'entrées disponibles via la propriété statique PerformanceObserver.supportedEntryTypes.

Observer les entrées qui se sont déjà produites

Par défaut, les objets PerformanceObserver ne peuvent observer les entrées que lorsqu'elles se produisent. Cela peut poser des problèmes si vous souhaitez effectuer un chargement différé de votre code d'analyse des performances afin de ne pas bloquer les ressources prioritaires.

Pour obtenir les entrées historiques (après qu'elles se sont produites), définissez l'option buffered sur true lorsque vous appelez observe(). Le navigateur inclut les entrées historiques de son tampon d'entrée des performances la première fois que votre rappel PerformanceObserver est appelé.

po.observe({
  type: 'some-entry-type',
  buffered: true,
});

Anciennes API de performance à éviter

Avant l'API Performance Observer, les développeurs pouvaient accéder aux entrées de performances à l'aide des trois méthodes suivantes définies sur l'objet performance:

Bien que ces API soient toujours compatibles, leur utilisation n'est pas recommandée, car elles ne vous permettent pas d'écouter quand de nouvelles entrées sont émises. De plus, de nombreuses nouvelles API (telles que les longues tâches) ne sont pas exposées via l'objet performance. Elles ne le sont que via PerformanceObserver.

À moins que vous n'ayez spécifiquement besoin d'une compatibilité avec Internet Explorer, il est préférable d'éviter ces méthodes dans votre code et d'utiliser PerformanceObserver à l'avenir.

API User Timing

L'API User Timing est une API de mesure à usage général pour les métriques temporelles. Il vous permet de marquer arbitrairement des points dans le temps, puis de mesurer ultérieurement la durée entre ces marques.

// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();

// Record the time immediately after running a task.
performance.mark('myTask:end');

// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');

Bien que des API telles que Date.now() ou performance.now() offrent des fonctionnalités similaires, l'API User Timing offre l'avantage suivant : elle s'intègre bien aux outils de gestion des performances. Par exemple, les outils pour les développeurs Chrome permettent de visualiser les mesures du temps utilisateur dans le panneau "Performances". De plus, de nombreux fournisseurs de solutions d'analyse suivent automatiquement toutes les mesures que vous effectuez et envoient les données de durée à leur backend d'analyse.

Pour générer des rapports sur les mesures du temps utilisateur, vous pouvez utiliser PerformanceObserver et vous inscrire pour observer des entrées de type measure:

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });

  // Start listening for `measure` entries to be dispatched.
  po.observe({type: 'measure', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

API Long Tasks

Navigateurs pris en charge

  • 58
  • 79
  • x
  • x

Source

L'API Long Tasks permet de savoir quand le thread principal du navigateur est bloqué suffisamment longtemps pour affecter la fréquence d'images ou la latence d'entrée. L'API signale les tâches exécutées pendant plus de 50 millisecondes.

Chaque fois que vous devez exécuter du code coûteux, ou charger et exécuter des scripts volumineux, il est utile de vérifier si ce code bloque le thread principal. En fait, de nombreuses métriques de niveau supérieur reposent sur l'API Long Tasks elle-même (telles que Time to Interactive (TTI) et Total Blocking Time (TBT)).

Pour déterminer à quel moment les longues tâches se produisent, vous pouvez utiliser PerformanceObserver et vous inscrire pour observer des entrées de type longtask:

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });

  // Start listening for `longtask` entries to be dispatched.
  po.observe({type: 'longtask', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

API Element Timing

Navigateurs pris en charge

  • 77
  • 79
  • x
  • x

Source

La métrique LCP (Largest Contentful Paint) est utile pour savoir à quel moment la plus grande image ou le plus grand bloc de texte a été affiché à l'écran, mais dans certains cas, vous souhaitez mesurer le délai d'affichage d'un autre élément.

Dans ce cas, utilisez l'API Element Timing. L'API LCP repose en réalité sur l'API Element Timing et ajoute des rapports automatiques sur le plus grand élément Contentful. Toutefois, vous pouvez également générer des rapports sur d'autres éléments en leur ajoutant explicitement l'attribut elementtiming et en enregistrant un PerformanceObserver pour surveiller le type d'entrée element.

<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->

<script>
  // Catch errors since some browsers throw when using the new `type` option.
  // https://bugs.webkit.org/show_bug.cgi?id=209216
  try {
    // Create the performance observer.
    const po = new PerformanceObserver((entryList) => {
      for (const entry of entryList.getEntries()) {
        // Log the entry and all associated details.
        console.log(entry.toJSON());
      }
    });

    // Start listening for `element` entries to be dispatched.
    po.observe({type: 'element', buffered: true});
  } catch (e) {
    // Do nothing if the browser doesn't support this API.
  }
</script>

API Event Timing

La métrique Interaction to Next Paint (INP) évalue la réactivité globale de la page en observant toutes les interactions liées aux clics, aux appuis et au clavier tout au long de la page. L'INP d'une page est le plus souvent l'interaction qui a pris le plus de temps, entre le moment où l'utilisateur a initié l'interaction jusqu'au moment où le navigateur affiche le frame suivant montrant le résultat visuel de l'entrée utilisateur.

La métrique INP est disponible grâce à l'API Event Timing. Cette API expose un certain nombre de codes temporels qui surviennent pendant le cycle de vie de l'événement, y compris:

  • startTime: heure à laquelle le navigateur reçoit l'événement.
  • processingStart: heure à laquelle le navigateur peut commencer à traiter les gestionnaires d'événements pour l'événement.
  • processingEnd: heure à laquelle le navigateur termine l'exécution de tout le code synchrone initié à partir des gestionnaires d'événements pour cet événement.
  • duration: délai (arrondi à 8 millisecondes pour des raisons de sécurité) entre le moment où le navigateur reçoit l'événement et le moment où il peut afficher le frame suivant après la fin de l'exécution de tout le code synchrone initié à partir des gestionnaires d'événements.

L'exemple suivant montre comment utiliser ces valeurs pour créer des mesures personnalisées:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  const po = new PerformanceObserver((entryList) => {
    // Get the last interaction observed:
    const entries = Array.from(entryList.getEntries()).forEach((entry) => {
      // Get various bits of interaction data:
      const inputDelay = entry.processingStart - entry.startTime;
      const processingTime = entry.processingEnd - entry.processingStart;
      const duration = entry.duration;
      const eventType = entry.name;
      const target = entry.target || "(not set)"

      console.log("----- INTERACTION -----");
      console.log(`Input delay (ms): ${inputDelay}`);
      console.log(`Event handler time (ms): ${processingTime}`);
      console.log(`Total event duration (ms): ${duration}`);
      console.log(`Event type: ${eventType}`);
      console.log(target);
    });
  });

  // A durationThreshold of 16ms is necessary to surface more
  // interactions, since the default is 104ms. The minimum
  // durationThreshold is 16ms.
  po.observe({type: 'event', buffered: true, durationThreshold: 16});
} catch (error) {
  // Do nothing if the browser doesn't support this API.
}

API Resource Timing

L'API Resource Timing fournit aux développeurs des informations détaillées sur le chargement des ressources d'une page spécifique. Malgré le nom de l'API, les informations qu'elle fournit ne se limitent pas aux données temporelles (bien qu'il y en ait beaucoup). Les autres données auxquelles vous pouvez accéder incluent:

  • initiatorType : manière dont la ressource a été récupérée, par exemple à partir d'un tag <script> ou <link>, ou d'un appel fetch().
  • nextHopProtocol: protocole utilisé pour récupérer la ressource (par exemple h2 ou quic).
  • encodedBodySize/decodedBodySize]: taille de la ressource sous sa forme encodée ou décodée (respectivement)
  • transferSize: taille de la ressource réellement transférée sur le réseau. Lorsque les ressources sont traitées par le cache, cette valeur peut être nettement inférieure à encodedBodySize et, dans certains cas, être égale à zéro (si aucune revalidation du cache n'est requise).

Vous pouvez utiliser la propriété transferSize des entrées de durée des ressources pour mesurer une métrique de taux de succès de cache ou de taille totale de la ressource mise en cache, ce qui peut être utile pour comprendre l'impact de votre stratégie de mise en cache des ressources sur les performances des visiteurs récurrents.

L'exemple suivant consigne toutes les ressources demandées par la page et indique si chaque ressource a été traitée ou non par le cache.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log(entry.name, entry.transferSize === 0);
    }
  });

  // Start listening for `resource` entries to be dispatched.
  po.observe({type: 'resource', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Navigateurs pris en charge

  • 57
  • 12
  • 58
  • 15

Source

L'API Navigation Timing est semblable à l'API Resource Timing, mais elle ne génère que des rapports sur les requêtes de navigation. Le type d'entrée navigation est également semblable au type d'entrée resource, mais il contient des informations supplémentaires spécifiques aux seules requêtes de navigation (par exemple, le déclenchement des événements DOMContentLoaded et load).

Une métrique que de nombreux développeurs suivent pour comprendre le temps de réponse du serveur (Time to First Byte (TTFB)) est disponible à l'aide de l'API Navigation Timing. Il s'agit en particulier de l'horodatage responseStart de l'entrée.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log('Time to first byte', entry.responseStart);
    }
  });

  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Le temps de démarrage du service worker pour les requêtes de navigation est une autre métrique qui peut intéresser les développeurs qui utilisent un service worker. Il s'agit du temps nécessaire au navigateur pour démarrer le thread de service worker avant de pouvoir commencer à intercepter des événements de récupération.

Le temps de démarrage du service worker pour une requête de navigation particulière peut être déterminé à partir du delta entre entry.responseStart et entry.workerStart.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      console.log('Service Worker startup time:',
          entry.responseStart - entry.workerStart);
    }
  });

  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

API Server Timing

L'API Server Timing vous permet de transmettre des données temporelles spécifiques aux requêtes depuis votre serveur vers le navigateur via des en-têtes de réponse. Par exemple, vous pouvez indiquer le temps nécessaire pour rechercher des données dans une base de données pour une requête particulière, ce qui peut être utile pour déboguer les problèmes de performances causés par la lenteur du serveur.

Pour les développeurs qui font appel à des fournisseurs de solutions d'analyse tiers, l'API Server Timing est le seul moyen de corréler les données sur les performances du serveur avec d'autres métriques commerciales que ces outils d'analyse sont susceptibles de mesurer.

Pour spécifier des données de temps de serveur dans vos réponses, vous pouvez utiliser l'en-tête de réponse Server-Timing. Voici un exemple.

HTTP/1.1 200 OK

Server-Timing: miss, db;dur=53, app;dur=47.2

Ensuite, à partir de vos pages, vous pouvez lire ces données dans les entrées resource ou navigation des API Resource Timing et Navigation Timing.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Logs all server timing data for this response
      console.log('Server Timing', entry.serverTiming);
    }
  });

  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}