Metriche personalizzate

È molto utile disporre di metriche incentrate sull'utente che puoi misurare in modo universale su qualsiasi sito web. Queste metriche ti consentono di:

  • Scopri in che modo gli utenti reali utilizzano il web nel suo complesso.
  • Confronta il tuo sito con quello di un concorrente.
  • Monitora dati utili e strategici nei tuoi strumenti di analisi senza dover scrivere codice personalizzato.

Le metriche universali offrono una buona base di riferimento, ma in molti casi è necessario misurare di più rispetto a queste metriche per acquisire l'esperienza completa per il tuo sito specifico.

Le metriche personalizzate ti consentono di misurare aspetti dell'esperienza sul tuo sito che potrebbero essere applicabili solo al tuo sito, ad esempio:

  • Il tempo necessario a un'app a pagina singola (APS) per passare da una "pagina" all'altra.
  • Il tempo necessario a una pagina per visualizzare i dati recuperati da un database per gli utenti che hanno eseguito l'accesso.
  • Il tempo necessario per idratare un'app con rendering lato server (SSR).
  • La percentuale di hit della cache per le risorse caricate dai visitatori di ritorno.
  • La latenza degli eventi di clic o tastiera in un gioco.

API per misurare le metriche personalizzate

In passato, gli sviluppatori web non disponevano di molte API di basso livello per misurare il rendimento e, di conseguenza, hanno dovuto ricorrere a hack per misurare il rendimento di un sito.

Ad esempio, è possibile determinare se il thread principale è bloccato a causa di attività JavaScript di lunga durata eseguendo un ciclo requestAnimationFrame e calcolando il delta tra ogni frame. Se il delta è notevolmente superiore alla frequenza frame del display, puoi segnalarlo come attività lunga. Tuttavia, questi hack non sono consigliati perché influiscono sulle prestazioni stesse (ad esempio scaricando la batteria).

La prima regola per una misurazione efficace del rendimento è assicurarsi che le tecniche di misurazione del rendimento non causino problemi di rendimento. Pertanto, per qualsiasi metrica personalizzata che misuri sul tuo sito, è meglio utilizzare una delle seguenti API, se possibile.

API Performance Observer

Supporto dei browser

  • Chrome: 52.
  • Edge: 79.
  • Firefox: 57.
  • Safari: 11.

Origine

L'API Performance Observer è il meccanismo che raccoglie e mostra i dati di tutte le altre API di rendimento discusse in questa pagina. Comprendere questo aspetto è fondamentale per ottenere dati di qualità.

Puoi utilizzare PerformanceObserver per iscriverti passivamente agli eventi relativi al rendimento. In questo modo, i callback dell'API vengono attivati durante i periodi di inattività, il che significa che in genere non interferiscono con il rendimento della pagina.

Per creare un PerformanceObserver, passa un callback da eseguire ogni volta che vengono inviate nuove voci sul rendimento. Poi, indica all'osservatore i tipi di voci da ascoltare utilizzando il metodo observe():

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'});

Le sezioni seguenti elencano tutti i vari tipi di voci disponibili per l'osservazione, ma nei browser più recenti puoi controllare quali tipi di voci sono disponibili tramite la proprietà statica PerformanceObserver.supportedEntryTypes.

Osservare le voci che si sono già verificate

Per impostazione predefinita, gli oggetti PerformanceObserver possono osservare le voci solo quando si verificano. Ciò può causare problemi se vuoi eseguire il caricamento lento del codice di analisi del rendimento in modo che non blocchi le risorse con priorità più elevata.

Per ottenere le voci storiche (dopo che si sono verificate), imposta il flag buffered su true quando chiami observe(). Il browser includerà le voci storiche del proprio buffer delle voci sul rendimento la prima volta che viene chiamato il tuo PerformanceObserver callback, fino alle dimensioni massime del buffer per quel tipo.

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

API di prestazioni precedenti da evitare

Prima dell'API Performance Observer, gli sviluppatori potevano accedere alle voci sul rendimento utilizzando i seguenti tre metodi definiti nell'oggetto performance:

Sebbene queste API siano ancora supportate, il loro utilizzo non è consigliato perché non ti consentono di ascoltare quando vengono emesse nuove voci. Inoltre, molte nuove API (ad esempio largest-contentful-paint) non sono esposte tramite l'oggetto performance, ma solo tramite PerformanceObserver.

A meno che tu non abbia bisogno specificamente della compatibilità con Internet Explorer, ti consigliamo di evitare questi metodi nel codice e di utilizzare PerformanceObserver in futuro.

API User Timing

Supporto dei browser

  • Chrome: 28.
  • Edge: 12.
  • Firefox: 38.
  • Safari: 11.

Origine

L'API User Timing è un'API di misurazione di uso generale per le metriche basate sul tempo. Ti consente di contrassegnare arbitrariamente dei punti nel tempo e di misurare in un secondo momento la durata tra questi punti.

// 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');

Sebbene API come Date.now() o performance.now() offrano funzionalità simili, il vantaggio dell'utilizzo dell'API User Timing è che si integra bene con gli strumenti di misurazione del rendimento. Ad esempio, Chrome DevTools visualizza le misurazioni dei tempi dell'utente nel riquadro Rendimento e molti fornitori di servizi di analisi monitorano automaticamente anche le misurazioni effettuate e inviano i dati sulla durata al proprio backend di analisi.

Per registrare le misurazioni di Tempo di utilizzo, puoi utilizzare PerformanceObserver e registrarti per osservare le voci di tipo measure:

// 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});

API Long Tasks

Supporto dei browser

  • Chrome: 58.
  • Edge: 79.
  • Firefox: non supportato.
  • Safari: non supportato.

Origine

L'API Long Tasks è utile per sapere quando il thread principale del browser è bloccato per un periodo di tempo sufficiente a influire sulla frequenza dei fotogrammi o sulla latenza di input. L'API segnalerà tutte le attività che vengono eseguite per più di 50 millisecondi.

Ogni volta che devi eseguire codice costoso o caricare ed eseguire script di grandi dimensioni, è utile monitorare se il codice blocca il thread principale. Infatti, molte metriche di livello superiore sono basate sull'API Long Tasks stessa (ad esempio Time to Interactive (TTI) e Total Blocking Time (TBT)).

Per determinare quando si verificano attività lunghe, puoi utilizzare PerformanceObserver e registrarti per osservare le voci di tipo longtask:

// 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});

API Long Animation Frames

Supporto dei browser

  • Chrome: 123.
  • Edge: 123.
  • Firefox: non supportato.
  • Safari: non supportato.

Origine

L'API Long Animation Frames è una nuova versione dell'API Long Tasks che esamina i frame lunghi, anziché le attività lunghe, di oltre 50 millisecondi. In questo modo vengono risolti alcuni mancamenti dell'API Long Tasks, tra cui una migliore attribuzione e un ambito più ampio di ritardi potenzialmente problematici.

Per determinare quando si verificano frame lunghi, puoi utilizzare PerformanceObserver e registrarti per osservare le voci di tipo long-animation-frame:

// 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 `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});

API Element Timing

Supporto dei browser

  • Chrome: 77.
  • Edge: 79.
  • Firefox: non supportato.
  • Safari: non supportato.

Origine

La metrica Largest Contentful Paint (LCP) è utile per sapere quando l'immagine o il blocco di testo più grande è stato visualizzato sullo schermo, ma in alcuni casi è necessario misurare il tempo di rendering di un elemento diverso.

In questi casi, utilizza l'API Element Timing. L'API LCP è in realtà basata sull'API Element Timing e aggiunge i report automatici dell'elemento con contenuti più grande, ma puoi anche generare report su altri elementi aggiungendo esplicitamente l'attributo elementtiming e registrando un PerformanceObserver per osservare il tipo di voce element.

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

<script>
  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});
</script>

API Event Timing

Supporto dei browser

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 89.
  • Safari: non supportato.

Origine

La metrica Interaction to Next Paint (INP) valuta l'adattabilità complessiva della pagina osservando tutti i clic, tocchi e interazioni da tastiera durante la vita di una pagina. L'INP di una pagina è spesso l'interazione che ha richiesto più tempo per essere completata, dal momento in cui l'utente ha avviato l'interazione al momento in cui il browser ha visualizzato il frame successivo che mostra il risultato visivo dell'input dell'utente.

La metrica INP è resa possibile dall'API Event Timing. Questa API espone una serie di timestamp che si verificano durante il ciclo di vita dell'evento, tra cui:

  • startTime: l'ora in cui il browser riceve l'evento.
  • processingStart: il momento in cui il browser è in grado di iniziare a elaborare i gestori di eventi per l'evento.
  • processingEnd: il momento in cui il browser termina l'esecuzione di tutto il codice sincrono avviato dai gestori eventi per questo evento.
  • duration: il tempo (arrotondato a 8 millisecondi per motivi di sicurezza) che intercorre tra il momento in cui il browser riceve l'evento e il momento in cui è in grado di disegnare il frame successivo dopo aver completato l'esecuzione di tutto il codice sincrono avviato dai gestori di eventi.

L'esempio seguente mostra come utilizzare questi valori per creare misurazioni personalizzate:

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 presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
    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 processing time (ms): ${processingTime}`);
    console.log(`Presentation delay (ms): ${presentationDelay}`);
    console.log(`Total event duration (ms): ${duration}`);
    console.log(`Event type: ${eventType}`);
    console.log(target);
  });
});

// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});

API Resource Timing

Supporto dei browser

  • Chrome: 29.
  • Edge: 12.
  • Firefox: 35.
  • Safari: 11.

Origine

L'API Resource Timing fornisce agli sviluppatori informazioni dettagliate su come sono state caricate le risorse per una determinata pagina. Nonostante il nome dell'API, le informazioni fornite non si limitano ai dati sui tempi (anche se ce ne sono molti). Altri dati a cui puoi accedere includono:

  • initiatorType: la modalità di recupero della risorsa, ad esempio da un tag <script> o <link> o da una chiamata fetch().
  • nextHopProtocol: il protocollo utilizzato per recuperare la risorsa, ad esempio h2 o quic.
  • encodedBodySize/decodedBodySize]: le dimensioni della risorsa nella forma codificata o decodificata (rispettivamente)
  • transferSize: le dimensioni della risorsa effettivamente trasferita sulla rete. Quando le risorse vengono soddisfatte dalla cache, questo valore può essere molto inferiore a encodedBodySize e, in alcuni casi, può essere pari a zero (se non è richiesta la convalida della cache).

Puoi utilizzare la proprietà transferSize delle voci di temporizzazione delle risorse per misurare una metrica tasso di hit della cache o una metrica dimensioni totali delle risorse memorizzate nella cache, che può essere utile per capire in che modo la tua strategia di memorizzazione nella cache delle risorse influisce sul rendimento per i visitatori abituali.

L'esempio seguente registra tutte le risorse richieste dalla pagina e indica se ogni risorsa è stata soddisfatta dalla cache o meno.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log(entry.name, entry.transferSize === 0);
  }
});

// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});

Supporto dei browser

  • Chrome: 57.
  • Edge: 12.
  • Firefox: 58.
  • Safari: 15.

Origine

L'API Navigation Timing è simile all'API Resource Timing, ma genera report solo sulle richieste di navigazione. Anche il tipo di voce navigation è simile al tipo di voce resource, ma contiene alcune informazioni aggiuntive specifiche solo per le richieste di navigazione (ad esempio quando vengono attivati gli eventi DOMContentLoaded e load).

Una metrica monitorata da molti sviluppatori per comprendere il tempo di risposta del server (tempo per primo byte (TTFB)) è disponibile utilizzando l'API Navigation Timing, in particolare il timestamp responseStart della voce.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log('Time to first byte', entry.responseStart);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});

Un'altra metrica che potrebbe interessare agli sviluppatori che utilizzano i service worker è il tempo di avvio del service worker per le richieste di navigazione. Si tratta del tempo necessario al browser per avviare il thread del servizio worker prima di poter iniziare a intercettare gli eventi di recupero.

Il momento di avvio del service worker per una determinata richiesta di navigazione può essere determinato dal delta tra entry.responseStart e entry.workerStart.

// 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});

API Server Timing

Supporto dei browser

  • Chrome: 65.
  • Edge: 79.
  • Firefox: 61.
  • Safari: 16.4.

Origine

L'API Server Timing ti consente di passare i dati relativi ai tempi specifici della richiesta dal server al browser tramite le intestazioni di risposta. Ad esempio, puoi indicare il tempo necessario per cercare i dati in un database per una determinata richiesta, il che può essere utile per il debug dei problemi di prestazioni causati dalla lentezza del server.

Per gli sviluppatori che utilizzano provider di analisi di terze parti, l'API Server Timing è l'unico modo per correlare i dati sulle prestazioni del server con altre metriche aziendali che questi strumenti di analisi potrebbero misurare.

Per specificare i dati relativi ai tempi del server nelle risposte, puoi utilizzare l'intestazione di risposta Server-Timing. Ecco un esempio.

HTTP/1.1 200 OK

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

Poi, dalle tue pagine, puoi leggere questi dati sia nelle voci resource che navigation delle API Resource Timing e Navigation Timing.

// 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});