Scopri come trovare interazioni lente nei dati dei campi del tuo sito web per trovare opportunità di miglioramento di Interaction to Next Paint.
I dati sul campo sono dati che ti indicano l'esperienza degli utenti reali sul tuo sito web. Evidenzia i problemi che non puoi trovare solo nei dati di laboratorio. Per quanto riguarda l'Interaction to Next Paint (INP), i dati dei campi sono essenziali per identificare le interazioni lente e forniscono indizi fondamentali per aiutarti a correggerle.
In questa guida imparerai a valutare rapidamente l'INP del tuo sito web utilizzando i dati sul campo del Chrome User Experience Report (CrUX) per capire se il tuo sito web presenta problemi con l'INP. Successivamente, imparerai a utilizzare la build di attribuzione della libreria JavaScript web-vitals e i nuovi insight che fornisce dall'API Long Animation Frames (LoAF) per raccogliere e interpretare i dati dei campi per le interazioni lente sul tuo sito web.
Inizia con CrUX per valutare l'INP del tuo sito web
Se non raccogli dati sul campo dagli utenti del tuo sito web, CrUX potrebbe essere un buon punto di partenza. CrUX raccoglie i dati sul campo di utenti Chrome reali che hanno attivato l'invio di dati di telemetria.
I dati di CrUX vengono visualizzati in diverse aree, a seconda dell'ambito delle informazioni che stai cercando. CrUX può fornire dati su INP e altri Core Web Vitals per:
- Singole pagine e interi domini utilizzando PageSpeed Insights.
- Tipi di pagine. Ad esempio, molti siti web di e-commerce hanno tipi di pagine dei dettagli del prodotto e pagine di schede di prodotto. Puoi ottenere i dati di CrUX per tipi di pagina unici in Search Console.
Come punto di partenza, puoi inserire l'URL del tuo sito web in PageSpeed Insights. Una volta inserito l'URL, i relativi dati dei campi, se disponibili, verranno visualizzati per più metriche, tra cui INP. Puoi anche utilizzare i pulsanti di attivazione/disattivazione per controllare i valori INP per le dimensioni mobile e desktop.
Questi dati sono utili perché ti dicono se hai un problema. Tuttavia, CrUX non può dirti cosa sta causando i problemi. Sono disponibili molte soluzioni di monitoraggio degli utenti reali (RUM) che ti aiuteranno a raccogliere i tuoi dati dei campi dagli utenti del tuo sito web per rispondere a questa domanda. Un'opzione è raccogliere questi dati dei campi autonomamente utilizzando la libreria JavaScript web-vitals.
Raccogli i dati dei campi con la libreria JavaScript web-vitals
La libreria JavaScript web-vitals è uno script che puoi caricare sul tuo sito web per raccogliere dati sul campo dagli utenti del tuo sito web. Puoi utilizzarla per registrare una serie di metriche, tra cui INP nei browser che la supportano.
La build standard della libreria web-vitals può essere utilizzata per ottenere dati INP di base dagli utenti sul campo:
import {onINP} from 'web-vitals';
onINP(({name, value, rating}) => {
console.log(name); // 'INP'
console.log(value); // 512
console.log(rating); // 'poor'
});
Per analizzare i dati sul campo dei tuoi utenti, devi inviarli da qualche parte:
import {onINP} from 'web-vitals';
onINP(({name, value, rating}) => {
// Prepare JSON to be sent for collection. Note that
// you can add anything else you'd want to collect here:
const body = JSON.stringify({name, value, rating});
// Use `sendBeacon` to send data to an analytics endpoint.
// For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
navigator.sendBeacon('/analytics', body);
});
Tuttavia, questi dati da soli non dicono molto di più di quanto farebbe CrUX. È qui che entra in gioco la build di attribuzione della libreria web-vitals.
Vai oltre con la build di attribuzione della libreria web-vitals
La build di attribuzione della libreria web-vitals mostra dati aggiuntivi che puoi ottenere dagli utenti sul campo per aiutarti a risolvere meglio i problemi delle interazioni problematiche che influiscono sull'INP del tuo sito web. Questi dati sono accessibili tramite l'oggetto attribution mostrato nel metodo onINP() della libreria:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, rating, attribution}) => {
console.log(name); // 'INP'
console.log(value); // 56
console.log(rating); // 'good'
console.log(attribution); // Attribution data object
});
Oltre all'INP della pagina, la build di attribuzione fornisce molti dati che puoi utilizzare per comprendere i motivi delle interazioni lente, tra cui la parte dell'interazione su cui concentrarti. Può aiutarti a rispondere a domande importanti quali:
- "L'utente ha interagito con la pagina durante il caricamento?"
- "I gestori di eventi dell'interazione sono stati eseguiti per molto tempo?"
- "Il codice del gestore di eventi di interazione è stato avviato in ritardo? In tal caso, cos'altro stava succedendo nel thread principale in quel momento?"
- "L'interazione ha causato un'elevata quantità di lavoro di rendering che ha ritardato la visualizzazione del frame successivo?"
La tabella seguente mostra alcuni dei dati di attribuzione di base che puoi ottenere dalla libreria e che possono aiutarti a capire alcune cause di alto livello delle interazioni lente sul tuo sito web:
Chiave oggetto attribution
|
Dati |
|---|---|
interactionTarget
|
Un selettore CSS che rimanda all'elemento che ha generato il valore INP della pagina, ad esempio button#save.
|
interactionType
|
Il tipo di interazione, ovvero clic, tocchi o input da tastiera. |
inputDelay*
|
Il ritardo di input dell'interazione. |
processingDuration*
|
Il tempo che intercorre tra l'avvio dell'esecuzione del primo listener di eventi in risposta all'interazione dell'utente e il completamento di tutta l'elaborazione del listener di eventi. |
presentationDelay*
|
Il ritardo di presentazione dell'interazione, che si verifica dal momento in cui i gestori di eventi terminano fino al momento in cui viene visualizzato il frame successivo. |
longAnimationFrameEntries*
|
Voci del LoAF associate all'interazione. Per ulteriori informazioni, consulta la sezione successiva. |
A partire dalla versione 4 della libreria web-vitals, puoi ottenere informazioni ancora più approfondite sulle interazioni problematiche tramite i dati forniti con le suddivisioni delle fasi INP (ritardo di input, durata dell'elaborazione e ritardo di presentazione) e l'API Long Animation Frames (LoAF).
API Long Animation Frames (LoAF)
Il debug delle interazioni utilizzando i dati dei campi è un'attività complessa. Tuttavia, con i dati di LoAF, ora è possibile ottenere informazioni più dettagliate sulle cause delle interazioni lente, poiché LoAF espone una serie di tempistiche dettagliate e altri dati che puoi utilizzare per individuare le cause precise e, soprattutto, dove si trova l'origine del problema nel codice del tuo sito web.
La build di attribuzione della libreria web-vitals espone un array di voci LoAF nella chiave longAnimationFrameEntries dell'oggetto attribution. La tabella seguente elenca alcuni dati chiave che puoi trovare in ogni voce LoAF:
| Chiave dell'oggetto della voce LoAF | Dati |
|---|---|
duration
|
La durata del frame dell'animazione lungo, fino al termine del layout, ma escludendo la pittura e la composizione. |
blockingDuration
|
La quantità totale di tempo nel frame in cui il browser non è stato in grado di rispondere rapidamente a causa di attività lunghe. Questo tempo di blocco può includere attività lunghe che eseguono JavaScript, nonché qualsiasi attività di rendering lunga successiva nel frame. |
firstUIEventTimestamp
|
Il timestamp di quando l'evento è stato inserito in coda durante il frame. Utile per capire l'inizio del ritardo di input di un'interazione. |
startTime
|
Il timestamp di inizio del frame. |
renderStart
|
Quando è iniziato il lavoro di rendering del frame. Sono inclusi i callback requestAnimationFrame (e i callback ResizeObserver, se applicabili), ma potenzialmente prima dell'inizio di qualsiasi lavoro di stile/layout.
|
styleAndLayoutStart
|
Quando si verifica un'operazione di stile/layout nel frame. Può essere utile per determinare la durata del lavoro di stile/layout quando si considerano altri timestamp disponibili. |
scripts
|
Un array di elementi contenenti informazioni sull'attribuzione degli script che contribuiscono all'INP della pagina. |
blockingDuration).
Tutte queste informazioni possono dirti molto su ciò che rende lenta un'interazione, ma l'array scripts mostrato dalle voci LoAF dovrebbe essere di particolare interesse:
| Chiave dell'oggetto di attribuzione dello script | Dati |
|---|---|
invoker
|
L'invoker. Questo può variare in base al tipo di invoker descritto nella riga successiva. Esempi di invoker possono essere valori come 'IMG#id.onload', 'Window.requestAnimationFrame' o 'Response.json.then'. |
invokerType
|
Il tipo di invoker. Può essere 'user-callback', 'event-listener', 'resolve-promise', 'reject-promise', 'classic-script' o 'module-script'.
|
sourceURL
|
L'URL dello script da cui ha avuto origine il frame dell'animazione lungo. |
sourceCharPosition
|
La posizione del carattere nello script identificata da sourceURL.
|
sourceFunctionName
|
Il nome della funzione nello script identificato. |
Ogni voce di questo array contiene i dati mostrati in questa tabella, che fornisce informazioni sullo script responsabile dell'interazione lenta e sul motivo per cui lo è stato.
Misura e identifica le cause comuni delle interazioni lente
Per darti un'idea di come potresti utilizzare queste informazioni, questa guida illustrerà come utilizzare i dati LoAF mostrati nella libreria web-vitals per determinare alcune cause alla base delle interazioni lente.
Tempi di elaborazione lunghi
La durata dell'elaborazione di un'interazione è il tempo necessario per l'esecuzione completa dei callback del gestore di eventi registrati dell'interazione e di qualsiasi altra cosa che potrebbe verificarsi nel frattempo. Le durate di elaborazione elevate vengono rilevate dalla libreria web-vitals:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {processingDuration} = attribution; // 512.5
});
È naturale pensare che la causa principale di un'interazione lenta sia il tempo impiegato dal codice del gestore di eventi per l'esecuzione, ma non è sempre così. Una volta confermato che questo è il problema, puoi approfondire con i dati LoAF:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {processingDuration} = attribution; // 512.5
// Get the longest script from LoAF covering `processingDuration`:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
if (script) {
// Get attribution for the long-running event handler:
const {invokerType} = script; // 'event-listener'
const {invoker} = script; // 'BUTTON#update.onclick'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Come puoi vedere nello snippet di codice precedente, puoi utilizzare i dati LoAF per individuare la causa precisa di un'interazione con valori di durata dell'elaborazione elevati, tra cui:
- L'elemento e il relativo listener di eventi registrato.
- Il file di script e la posizione del carattere al suo interno contenente il codice del gestore di eventi a lunga esecuzione.
- Il nome della funzione.
Questo tipo di dati è di valore inestimabile. Non devi più fare il lavoro di ricerca per scoprire esattamente quale interazione o quale dei suoi gestori di eventi è responsabile dei valori elevati della durata di elaborazione. Inoltre, poiché gli script di terze parti spesso possono registrare i propri gestori di eventi, puoi determinare se è stato il tuo codice a causare il problema. Per il codice su cui hai il controllo, ti consigliamo di esaminare l'ottimizzazione delle attività lunghe.
Ritardi di input lunghi
Sebbene i gestori di eventi a lunga esecuzione siano comuni, ci sono altre parti dell'interazione da considerare. Una parte si verifica prima della durata dell'elaborazione, nota come ritardo di input. Si tratta del tempo che intercorre tra l'avvio dell'interazione da parte dell'utente e il momento in cui iniziano a essere eseguiti i callback del gestore di eventi, che si verifica quando il thread principale sta già elaborando un'altra attività. La build di attribuzione della libreria web-vitals può indicarti la durata del ritardo di input per un'interazione:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
});
Se noti che alcune interazioni hanno ritardi di input elevati, devi capire cosa stava succedendo nella pagina al momento dell'interazione che ha causato il ritardo di input elevato. Spesso, questo dipende dal fatto che l'interazione sia avvenuta durante il caricamento della pagina o dopo.
È stato durante il caricamento della pagina?
Il thread principale è spesso più occupato durante il caricamento di una pagina. Durante questo periodo, vengono messi in coda ed elaborati tutti i tipi di attività e, se l'utente tenta di interagire con la pagina mentre tutto questo lavoro è in corso, l'interazione può essere ritardata. Le pagine che caricano molto codice JavaScript possono avviare il lavoro di compilazione e valutazione degli script, nonché l'esecuzione di funzioni che preparano una pagina per le interazioni degli utenti. Questo lavoro può essere d'intralcio se l'utente interagisce mentre si verifica questa attività. Puoi scoprire se questo è il caso degli utenti del tuo sito web:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
// Get the longest script from the first LoAF entry:
const loaf = attribution.longAnimationFrameEntries[0];
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
if (script) {
// Invoker types can describe if script eval blocked the main thread:
const {invokerType} = script; // 'classic-script' | 'module-script'
const {sourceLocation} = script; // 'https://example.com/app.js'
}
});
Se registri questi dati sul campo e noti ritardi di input elevati e tipi di invoker di 'classic-script' o 'module-script', è ragionevole affermare che gli script sul tuo sito richiedono molto tempo per la valutazione e bloccano il thread principale per un tempo sufficiente a ritardare le interazioni. Puoi ridurre questo tempo di blocco suddividendo gli script in bundle più piccoli, posticipando il caricamento del codice inizialmente non utilizzato in un secondo momento ed eseguendo l'audit del tuo sito per individuare il codice non utilizzato che puoi rimuovere del tutto.
È avvenuto dopo il caricamento della pagina?
Sebbene i ritardi di input si verifichino spesso durante il caricamento di una pagina, è altrettanto possibile che si verifichino dopo il caricamento di una pagina, per una causa completamente diversa. Le cause più comuni dei ritardi di input dopo il caricamento della pagina possono essere il codice che viene eseguito periodicamente a causa di una chiamata setInterval precedente o anche i callback di eventi che sono stati messi in coda per l'esecuzione in precedenza e sono ancora in fase di elaborazione.
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
// Get the longest script from the first LoAF entry:
const loaf = attribution.longAnimationFrameEntries[0];
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
if (script) {
const {invokerType} = script; // 'user-callback'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Come nel caso della risoluzione dei problemi relativi a valori elevati della durata di elaborazione, i ritardi di input elevati dovuti alle cause menzionate in precedenza ti forniranno dati dettagliati sull'attribuzione degli script. Tuttavia, ciò che cambia è che il tipo di invoker cambierà in base alla natura del lavoro che ha ritardato l'interazione:
'user-callback'indica che l'attività di blocco proveniva dasetInterval,setTimeouto persinorequestAnimationFrame.'event-listener'indica che l'attività di blocco proveniva da un input precedente che era stato messo in coda ed era ancora in fase di elaborazione.'resolve-promise'e'reject-promise'indicano che l'attività di blocco proveniva da un lavoro asincrono avviato in precedenza e risolto o rifiutato nel momento in cui l'utente ha tentato di interagire con la pagina, ritardando l'interazione.
In ogni caso, i dati di attribuzione dello script ti daranno un'idea di dove iniziare a cercare e se il ritardo di input è dovuto al tuo codice o a quello di uno script di terze parti.
Ritardi di presentazione lunghi
I ritardi di presentazione sono l'ultimo miglio di un'interazione e iniziano quando i gestori di eventi dell'interazione terminano, fino al momento in cui viene visualizzato il frame successivo. Si verificano quando il lavoro in un gestore di eventi dovuto a un'interazione modifica lo stato visivo dell'interfaccia utente. Come per le durate di elaborazione e i ritardi di input, la libreria web-vitals può indicarti la durata del ritardo di presentazione per un'interazione:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 113.32307691
});
Se registri questi dati e noti ritardi di presentazione elevati per le interazioni che contribuiscono all'INP del tuo sito web, i colpevoli possono variare, ma ecco un paio di cause da tenere d'occhio.
Lavoro di stile e layout costoso
I ritardi di presentazione lunghi possono essere dovuti a un costoso lavoro di ricalcolo dello stile e di layout che deriva da una serie di cause, tra cui selettori CSS complessi e dimensioni DOM elevate. Puoi misurare la durata di questo lavoro con le tempistiche LoAF mostrate nella libreria web-vitals:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 113.32307691
// Get the longest script from the last LoAF entry:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
// Get necessary timings:
const {startTime} = loaf; // 2120.5
const {duration} = loaf; // 1002
// Figure out the ending timestamp of the frame (approximate):
const endTime = startTime + duration; // 3122.5
// Get the start timestamp of the frame's style/layout work:
const {styleAndLayoutStart} = loaf; // 3011.17692309
// Calculate the total style/layout duration:
const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691
if (script) {
// Get attribution for the event handler that triggered
// the long-running style and layout operation:
const {invokerType} = script; // 'event-listener'
const {invoker} = script; // 'BUTTON#update.onclick'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
LoAF non ti dirà la durata del lavoro di stile e layout per un frame, ma ti dirà quando è iniziato. Con questo timestamp di inizio, puoi utilizzare altri dati di LoAF per calcolare una durata precisa di questo lavoro determinando l'ora di fine del frame e sottraendo il timestamp di inizio del lavoro di stile e layout.
Callback requestAnimationFrame a lunga esecuzione
Una potenziale causa di lunghi ritardi di presentazione è l'eccessivo lavoro svolto in un callback requestAnimationFrame. I contenuti di questo callback vengono eseguiti al termine dell'esecuzione dei gestori di eventi, ma appena prima del ricalcolo dello stile e del lavoro sul layout.
Il completamento di questi callback può richiedere molto tempo se il lavoro svolto al loro interno è complesso. Se sospetti che i valori elevati di ritardo della presentazione siano dovuti al lavoro che stai svolgendo con requestAnimationFrame, puoi utilizzare i dati LoAF mostrati dalla libreria web-vitals per identificare questi scenari:
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 543.1999999880791
// Get the longest script from the last LoAF entry:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
// Get the render start time and when style and layout began:
const {renderStart} = loaf; // 2489
const {styleAndLayoutStart} = loaf; // 2989.5999999940395
// Calculate the `requestAnimationFrame` callback's duration:
const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954
if (script) {
// Get attribution for the event handler that triggered
// the long-running requestAnimationFrame callback:
const {invokerType} = script; // 'user-callback'
const {invoker} = script; // 'FrameRequestCallback'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Se noti che una parte significativa del tempo di ritardo della presentazione viene impiegata in un callback requestAnimationFrame, assicurati che il lavoro che stai svolgendo in questi callback sia limitato all'esecuzione di un lavoro che comporta un aggiornamento effettivo dell'interfaccia utente. Qualsiasi altro lavoro che non tocchi il DOM o non aggiorni gli stili ritarderà inutilmente la visualizzazione del frame successivo, quindi fai attenzione.
Conclusione
I dati dei campi sono la migliore fonte di informazioni a cui puoi attingere per capire quali interazioni sono problematiche per gli utenti effettivi sul campo. Se ti affidi a strumenti di raccolta dei dati sul campo come la libreria JavaScript web-vitals (o un provider RUM), puoi avere maggiore certezza su quali interazioni sono più problematiche, per poi passare alla riproduzione delle interazioni problematiche in laboratorio e alla loro correzione.