Le metriche incentrate sull'utente che puoi misurare universalmente su qualsiasi sito web sono molto utili. Queste metriche ti consentono di:
- Comprendere l'esperienza degli utenti reali sul web nel suo complesso.
- Confronta il tuo sito con quello di un concorrente.
- Monitora dati utili e utilizzabili negli strumenti di analisi senza dover scrivere codice personalizzato.
Le metriche universali offrono una buona base di riferimento, ma in molti casi devi misurare più di queste metriche per acquisire l'esperienza completa per il tuo sito specifico.
Le metriche personalizzate ti consentono di misurare aspetti dell'esperienza del tuo sito che potrebbero essere applicabili solo al tuo sito, ad esempio:
- Il tempo necessario a un'app a pagina singola (SPA) 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 l'idratazione di un'app con rendering lato server (SSR).
- Il tasso di successo 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 le prestazioni e, di conseguenza, dovevano ricorrere a soluzioni alternative per misurare se un sito funzionava bene.
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 è significativamente più lungo del framerate del display, puoi segnalarlo come attività lunga. Tuttavia, questi trucchi non sono consigliati perché influiscono sulle prestazioni (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, è consigliabile utilizzare una delle seguenti API, se possibile.
API Performance Observer
L'API Performance Observer è il meccanismo che raccoglie e visualizza i dati di tutte le altre API di performance descritte in questa pagina. Comprenderlo è fondamentale per ottenere dati di qualità.
Puoi utilizzare PerformanceObserver per iscriverti passivamente agli eventi correlati al rendimento. In questo modo, i callback API vengono attivati durante i periodi di inattività, il che significa che di solito non interferiscono con il rendimento della pagina.
Per creare un PerformanceObserver, trasmetti un callback da eseguire ogni volta che vengono inviate nuove voci di rendimento. Poi, comunica all'osservatore quali tipi di voci 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 esaminare i tipi di voci disponibili tramite la proprietà statica PerformanceObserver.supportedEntryTypes.
Osservare le voci già avvenute
Per impostazione predefinita, gli oggetti PerformanceObserver possono osservare le voci solo quando si verificano. Ciò può causare problemi se vuoi caricare in modo differito il 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(). La prima volta che viene chiamata la tua funzione di callback PerformanceObserver, il browser includerà le voci storiche del buffer delle voci di rendimento, fino alle dimensioni massime del buffer per quel tipo.
po.observe({
type: 'some-entry-type',
buffered: true,
});
API per il rendimento precedenti da evitare
Prima dell'API Performance Observer, gli sviluppatori potevano accedere alle voci di rendimento utilizzando i tre metodi seguenti definiti nell'oggetto performance:
Sebbene queste API siano ancora supportate, il loro utilizzo non è consigliato perché non consentono di rilevare l'emissione di nuove voci. Inoltre, molte nuove API (come largest-contentful-paint) non vengono esposte tramite l'oggetto performance, ma solo tramite PerformanceObserver.
A meno che tu non abbia bisogno specificamente della compatibilità con Internet Explorer, è meglio evitare questi metodi nel codice e utilizzare PerformanceObserver in futuro.
API User Timing
L'API User Timing è un'API di misurazione per uso generico per le metriche basate sul tempo. Ti consente di contrassegnare arbitrariamente i punti nel tempo e misurare la durata tra questi 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, il vantaggio dell'utilizzo dell'API User Timing è che si integra bene con gli strumenti per il rendimento. Ad esempio, Chrome DevTools visualizza le misurazioni di User Timing nel riquadro Prestazioni e molti fornitori di analisi monitorano automaticamente anche le misurazioni effettuate e inviano i dati sulla durata al backend di analisi.
Per segnalare le misurazioni di User Timing, 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
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 si basano sulle API Long Tasks (come Tempo all'interattività (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
L'API Long Animation Frames è una nuova iterazione dell'API Long Tasks che esamina i frame lunghi, anziché le attività lunghe, di oltre 50 millisecondi. In questo modo vengono risolte alcune carenze 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
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 vuoi misurare il tempo di rendering di un elemento diverso.
Per questi casi, utilizza l'API Element Timing. L'API LCP è in realtà basata sull'API Element Timing e aggiunge la generazione automatica di report sull'elemento più grande con contenuti, 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
La metrica Interaction to Next Paint (INP) valuta l'adattabilità complessiva della pagina osservando tutti i clic, i tocchi e le interazioni da tastiera durante il ciclo di vita di una pagina. L'INP di una pagina è più 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 disegna 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: l'ora in cui il browser è in grado di iniziare l'elaborazione dei gestori di eventi per l'evento.processingEnd: ora 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 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 terminato l'esecuzione di tutto il codice sincrono avviato dai gestori di eventi.
Il seguente esempio 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
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 che fornisce non si limitano ai dati di temporizzazione (anche se ce ne sono molti). Altri dati a cui puoi accedere includono:
initiatorType: modalità di recupero della risorsa, ad esempio da un tag<script>o<link>oppure da una chiamatafetch().nextHopProtocol: il protocollo utilizzato per recuperare la risorsa, ad esempioh2oquic.encodedBodySize/decodedBodySize: la dimensione della risorsa nella sua forma codificata o decodificata (rispettivamente)transferSize: la dimensione della risorsa effettivamente trasferita sulla rete. Quando le risorse vengono soddisfatte dalla cache, questo valore può essere molto inferiore aencodedBodySizee in alcuni casi può essere zero (se non è richiesta alcuna convalida della cache).
Puoi utilizzare la proprietà transferSize delle voci di temporizzazione delle risorse per misurare una metrica percentuale di successi cache o una metrica dimensione totale delle risorse memorizzate nella cache, che può essere utile per capire in che modo la 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 o meno dalla cache.
// 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});
API Navigation Timing
L'API Navigation Timing è simile all'API Resource Timing, ma segnala solo le richieste di navigazione. Il tipo di voce navigation è simile anche 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 (Tempo per primo byte (TTFB)) è disponibile tramite 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 importante per gli sviluppatori che utilizzano 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 service worker prima che possa iniziare a intercettare gli eventi di recupero.
Il tempo di avvio del service worker per una determinata richiesta di navigazione può essere determinato dalla differenza 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
L'API Server Timing ti consente di trasmettere dati di temporizzazione specifici della richiesta dal server al browser tramite le intestazioni delle risposte. Ad esempio, puoi indicare il tempo impiegato per cercare 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 fornitori 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 di temporizzazione del server nelle risposte, puoi utilizzare l'intestazione della risposta Server-Timing. Ecco un esempio.
HTTP/1.1 200 OK
Server-Timing: miss, db;dur=53, app;dur=47.2
Dalle tue pagine, puoi leggere questi dati nelle voci resource o 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});