Metriche personalizzate

La disponibilità di metriche incentrate sull'utente universali che puoi misurare su qualsiasi sito web può essere molto utile per capire in che modo gli utenti utilizzano il web e per confrontare il tuo sito con quello della concorrenza. Tuttavia, in molti casi, per ottenere l'esperienza completa sul sito specifico, è necessario misurare non solo le metriche universali.

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

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

API per misurare metriche personalizzate

In passato gli sviluppatori web non avevano molte API di basso livello per misurare le prestazioni e, di conseguenza, hanno dovuto ricorrere a pirateria informatica per misurare il rendimento di un sito. Ad esempio, puoi determinare se il thread principale è bloccato da attività JavaScript a lunga esecuzione eseguendo un loop requestAnimationFrame e calcolando il delta tra ogni frame. Se il delta è molto più lungo della frequenza fotogrammi del display, puoi segnalarlo come attività lunga.

Tuttavia, attacchi di questo tipo possono influire sulle prestazioni del tuo sito, ad esempio consumando la batteria del dispositivo. Se le tue tecniche di misurazione del rendimento causano problemi di rendimento, i dati che fornirai non saranno accurati. Consigliamo quindi di utilizzare una delle API seguenti per creare metriche personalizzate.

API Performance Observationr

Supporto dei browser

  • 52
  • 79
  • 57
  • 11

Fonte

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

Puoi utilizzare PerformanceObserver per iscriverti passivamente agli eventi correlati al rendimento. In questo modo i callback delle API possono essere attivati durante i periodi di inattività, il che significa che solitamente non interferiscono con le prestazioni della pagina.

Quando crei un PerformanceObserver, passa un callback che viene eseguito ogni volta che vengono inviate nuove voci di rendimento. Quindi utilizza il metodo observe() per indicare all'osservatore quali tipi di voci ascoltare, come segue:

// Catch errors that 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.
}

Le sezioni seguenti elencano tutti i tipi di voci che puoi osservare. Nei browser più recenti, puoi anche controllare quali tipi di voci sono disponibili utilizzando la proprietà statica PerformanceObserver.supportedEntryTypes.

Osserva le voci già esistenti

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

Per recuperare le voci della cronologia, chiama observe con il flag buffered impostato su true. La prima volta che viene chiamato il callback PerformanceObserver, il browser include le voci cronologiche del buffer di inserimento delle prestazioni.

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

API legacy per le prestazioni da evitare

Prima dell'API Performance Observationr, gli sviluppatori potevano accedere alle voci relative alle prestazioni utilizzando i seguenti metodi definiti nell'oggetto performance. Sconsigliamo di usarle perché non ti permettono di ascoltare le nuove voci.

Inoltre, molte nuove API (come Attività lunghe) non sono esposte dall'oggetto performance, ma solo da PerformanceObserver. Pertanto, a meno che tu non abbia specificamente bisogno della compatibilità con Internet Explorer, ti consigliamo di evitare questi metodi nel codice e di utilizzare PerformanceObserver in futuro.

API User Timing

L'API User Timing è un'API di misurazione generica per le metriche basate sul tempo. Ti consente di contrassegnare in modo arbitrario i punti nel tempo per poi misurare la durata tra questi due segni in un secondo momento.

// 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, l'API User Timing si integra meglio con gli strumenti per le prestazioni. Ad esempio, Chrome DevTools mostra le misurazioni del tempo utente nel riquadro Prestazioni e molti provider di analisi monitorano automaticamente qualsiasi misurazione effettuata e inviano i dati sulla durata al proprio backend di analisi.

Per generare report sulle misurazioni del tempo utenti, registra un PerformanceObserver per osservare le voci di tipo measure:

// Catch errors 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.
  po.observe({type: 'measure', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

API Long Tasks

Supporto dei browser

  • 58
  • 79
  • x
  • x

Fonte

L'API Long Tasks è utile per determinare quando il thread principale del browser è bloccato per un tempo sufficiente a influire sulla frequenza frame o sulla latenza di input. L'API segnala tutte le attività eseguite per più di 50 millisecondi (ms).

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

Per determinare quando si verificano attività lunghe, registra un PerformanceObserver per osservare le voci di tipo 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

Supporto dei browser

  • 77
  • 79
  • x
  • x

Fonte

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

In questi casi, utilizza l'API Element Timing. L'API LCP è in realtà basata sull'API Element Timing e aggiunge report automatici sull'elemento con contenuti più grande, ma puoi anche creare report su altri elementi aggiungendo esplicitamente l'attributo elementtiming e registrando un PerformanceObservationr per osservare il tipo di voce 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 metrica First Input Delay (FID) misura il tempo che intercorre tra la prima interazione di un utente con una pagina e il momento in cui il browser può iniziare a elaborare i gestori di eventi in risposta a questa interazione. Tuttavia, in alcuni casi può essere utile anche misurare il tempo di elaborazione degli eventi.

Ciò è possibile utilizzando l'API Event Timing, che, oltre a misurare il FID, espone anche una serie di timestamp nel ciclo di vita dell'evento, tra cui:

  • startTime: l'ora in cui il browser riceve l'evento.
  • processingStart: l'ora in cui il browser può 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 di eventi per questo evento.
  • duration: il tempo (arrotondato a 8 ms per motivi di sicurezza) tra il momento in cui il browser riceve l'evento e il momento in cui è in grado di colorare 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:

// 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) => {
    const firstInput = entryList.getEntries()[0];

    // Measure First Input Delay (FID).
    const firstInputDelay = firstInput.processingStart - firstInput.startTime;

    // Measure the time it takes to run all event handlers
    // Doesn't include work scheduled asynchronously using methods like
    // `requestAnimationFrame()` or `setTimeout()`.
    const firstInputProcessingTime = firstInput.processingEnd - firstInput.processingStart;

    // Measure the entire duration of the event, from when input is received by
    // the browser until the next frame can be painted after processing all
    // event handlers.
    // Doesn't include work scheduled asynchronously using
    // `requestAnimationFrame()` or `setTimeout()`.
    // For security reasons, this value is rounded to the nearest 8 ms.
    const firstInputDuration = firstInput.duration;

    // Log these values to the console.
    console.log({
      firstInputDelay,
      firstInputProcessingTime,
      firstInputDuration,
    });
  });

  po.observe({type: 'first-input', buffered: true});
} catch (error) {
  // Do nothing if the browser doesn't support this API.
}

API Resource Timing

L'API Resource Timing offre agli sviluppatori insight dettagliati su come sono state caricate le risorse per una determinata pagina. A prescindere dal nome dell'API, le informazioni che fornisce non sono limitate ai dati di sincronizzazione (sebbene ce ne siano moltissime). Altri dati a cui puoi accedere includono:

  • initiatorType: come è stata recuperata la risorsa, ad esempio da un tag <script> o <link> o da fetch().
  • nextHopProtocol: il protocollo utilizzato per recuperare la risorsa, ad esempio h2 o quic.
  • encodedBodySize e decodedBodySize]: la dimensione della risorsa nella sua forma codificata o decodificata (rispettivamente).
  • transferSize: la dimensione della risorsa che è stata effettivamente trasferita sulla rete. Quando le risorse vengono esaurite mediante la cache, questo valore può essere molto inferiore a encodedBodySize e, in alcuni casi, può essere pari a zero, se non è richiesta alcuna riconvalida della cache.

Puoi utilizzare la proprietà transferSize delle voci di temporizzazione delle risorse per misurare una metrica di percentuale di successo della cache o una metrica di dimensione totale delle risorse memorizzate nella cache, utili per comprendere in che modo la strategia di memorizzazione nella cache delle risorse influisce sulle prestazioni dei visitatori abituali.

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

// Catch errors 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.
}

Supporto dei browser

  • 57
  • 12
  • 58
  • 15

Fonte

L'API Navigation Timing è simile all'API Resource Timing, ma riporta solo le 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 che molti sviluppatori monitorano per comprendere il tempo di risposta del server, Time to First Byte (TTFB), è disponibile tramite il timestamp responseStart nell'API Navigation Timing.

// Catch errors since  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 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Un'altra metrica che potrebbe interessare ai service worker è il tempo di avvio dei service worker per le richieste di navigazione. Si tratta del tempo impiegato dal browser per avviare il thread del worker di servizio prima che possa iniziare a intercettare gli eventi di recupero.

Il tempo di avvio del service worker per una richiesta di navigazione specificata può essere determinato dal delta tra entry.responseStart e entry.workerStart come segue:

// Catch errors 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 ti consente di passare dati di tempo specifici delle richieste dal tuo server al browser utilizzando le intestazioni delle risposte. Ad esempio, puoi indicare il tempo necessario per la ricerca dei dati in un database per una determinata richiesta, il che può essere utile per il debug dei problemi di prestazioni causati da 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 misurate da questi strumenti di analisi.

Per specificare i dati di tempo del server nelle risposte, utilizza l'intestazione della risposta Server-Timing. Esempio:

HTTP/1.1 200 OK

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

Successivamente, dalle pagine puoi leggere questi dati nelle voci resource o navigation delle API Resource Timing e Navigation Timing.

// Catch errors 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.
}