Informazioni su questo codelab
1. Introduzione
Una demo interattiva e un codelab per scoprire di più su Interaction to Next Paint (INP).
Prerequisiti
- Conoscenza dello sviluppo HTML e JavaScript.
- Consigliato: leggi la documentazione INP.
Cosa imparerai
- In che modo l'interazione tra le interazioni degli utenti e la gestione di queste interazioni influisce sull'adattabilità della pagina.
- Come ridurre ed eliminare i ritardi per un'esperienza utente fluida.
Cosa serve
- Un computer in grado di clonare il codice da GitHub ed eseguire comandi npm.
- Un editor di testo.
- Una versione recente di Chrome per il corretto funzionamento di tutte le misurazioni delle interazioni.
2. Configurazione
Recuperare ed eseguire il codice
Il codice si trova nel repository web-vitals-codelabs
.
- Clona il repository nel terminale:
git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
- Vai alla directory clonata:
cd web-vitals-codelabs/understanding-inp
- Installa le dipendenze:
npm ci
- Avvia il server web:
npm run start
- Visita la pagina http://localhost:5173/understanding-inp/ nel browser.
Panoramica dell'app
Nella parte superiore della pagina si trovano un contatore Punteggio e un pulsante Incremento. Una dimostrazione classica di reattività e risposta.
Sotto il pulsante sono presenti quattro misurazioni:
- INP: il punteggio INP attuale, che in genere corrisponde all'interazione peggiore.
- Interazione: il punteggio dell'interazione più recente.
- FPS: i frame al secondo del thread principale della pagina.
- Timer: un'animazione del timer in esecuzione per visualizzare il jank.
Le voci FPS e Timer non sono necessarie per misurare le interazioni. Vengono aggiunti solo per semplificare la visualizzazione della reattività.
Prova
Prova a interagire con il pulsante Aumenta e guarda il punteggio aumentare. I valori di INP e Interazione cambiano a ogni incremento?
INP misura il tempo che intercorre dal momento in cui l'utente interagisce fino a quando la pagina mostra effettivamente l'aggiornamento sottoposto a rendering all'utente.
3. Misurare le interazioni con Chrome DevTools
Apri DevTools dal menu Altri strumenti > Strumenti per sviluppatori, facendo clic con il tasto destro del mouse sulla pagina e selezionando Ispeziona o utilizzando una scorciatoia da tastiera.
Passa al riquadro Rendimento, che utilizzerai per misurare le interazioni.
Successivamente, acquisisci un'interazione nel riquadro Prestazioni.
- Premi il pulsante per registrare.
- Interagisci con la pagina (premi il pulsante Incrementa).
- Interrompi la registrazione.
Nella sequenza temporale risultante, troverai una traccia Interazioni. Espandilo facendo clic sul triangolo a sinistra.
Vengono visualizzate due interazioni. Aumenta lo zoom sulla seconda immagine scorrendo o tenendo premuto il tasto W.
Se passi il mouse sopra l'interazione, puoi vedere che è stata veloce, senza tempi di durata dell'elaborazione e con un tempo minimo di ritardo di input e ritardo di presentazione, la cui durata esatta dipende dalla velocità della macchina.
4. Listener di eventi di lunga durata
Apri il file index.js
e rimuovi il commento dalla funzione blockFor
all'interno del listener di eventi.
Vedi il codice completo: click_block.html
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
});
Salva il file. Il server vedrà la modifica e aggiornerà la pagina.
Prova a interagire di nuovo con la pagina. Le interazioni ora saranno notevolmente più lente.
Traccia del rendimento
Esegui un'altra registrazione nel riquadro Prestazioni per vedere come appare.
Quella che una volta era un'interazione breve ora richiede un secondo intero.
Quando passi il mouse sopra l'interazione, noterai che il tempo è quasi interamente dedicato alla "Durata dell'elaborazione", ovvero il tempo necessario per eseguire i callback del listener di eventi. Poiché la chiamata di blocco blockFor
si trova interamente all'interno del listener di eventi, è qui che viene impiegato il tempo.
5. Experiment: processing duration
Prova a riorganizzare il lavoro del listener di eventi per vedere l'effetto sull'INP.
Aggiorna prima l'interfaccia utente
Cosa succede se inverti l'ordine delle chiamate JS: aggiorni prima la UI, poi blocchi?
Visualizza il codice completo: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Hai notato che l'interfaccia utente è apparsa prima? L'ordine influisce sui punteggi INP?
Prova a eseguire una traccia ed esamina l'interazione per verificare se sono presenti differenze.
Listener separati
Cosa succede se sposti il lavoro in un listener di eventi separato? Aggiorna la UI in un listener di eventi e blocca la pagina da un listener separato.
Visualizza il codice completo: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
Che aspetto ha ora nel riquadro del rendimento?
Diversi tipi di eventi
La maggior parte delle interazioni attiverà molti tipi di eventi, dagli eventi del puntatore o della tastiera a eventi di passaggio del mouse, messa a fuoco/sfocatura ed eventi sintetici come beforechange e beforeinput.
Molte pagine reali hanno listener per molti eventi diversi.
Cosa succede se modifichi i tipi di eventi per i listener di eventi? Ad esempio, sostituire uno dei listener di eventi click
con pointerup
o mouseup
?
Visualizza il codice completo: diff_handlers.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Nessun aggiornamento dell'interfaccia utente
Cosa succede se rimuovi la chiamata per aggiornare l'interfaccia utente dal listener di eventi?
Visualizza il codice completo: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
6. Risultati dell'esperimento sulla durata dell'elaborazione
Traccia delle prestazioni: aggiorna prima l'interfaccia utente
Visualizza il codice completo: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Se esamini la registrazione di un pannello Rendimento in cui viene fatto clic sul pulsante, puoi notare che i risultati non sono cambiati. Sebbene un aggiornamento dell'interfaccia utente sia stato attivato prima del codice di blocco, il browser non ha aggiornato ciò che è stato visualizzato sullo schermo fino al completamento del listener di eventi, il che significa che l'interazione ha comunque richiesto poco più di un secondo per essere completata.
Traccia di rendimento: ascoltatori separati
Visualizza il codice completo: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
Anche in questo caso, non c'è alcuna differenza funzionale. L'interazione richiede comunque un secondo intero.
Se ingrandisci l'interazione con il clic, vedrai che vengono chiamate due funzioni diverse in seguito all'evento click
.
Come previsto, il primo, l'aggiornamento della UI, viene eseguito molto rapidamente, mentre il secondo richiede un secondo intero. Tuttavia, la somma dei loro effetti si traduce nella stessa interazione lenta per l'utente finale.
Traccia del rendimento: diversi tipi di eventi
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Questi risultati sono molto simili. L'interazione dura ancora un secondo intero; l'unica differenza è che il listener click
più breve per l'aggiornamento della UI viene eseguito dopo il listener di blocco pointerup
.
Traccia delle prestazioni: nessun aggiornamento della UI
Visualizza il codice completo: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
- Il punteggio non viene aggiornato, ma la pagina sì.
- Animazioni, effetti CSS, azioni predefinite dei componenti web (input del modulo), inserimento di testo ed evidenziazione del testo continuano a essere aggiornati.
In questo caso, il pulsante passa a uno stato attivo e torna indietro quando viene selezionato, il che richiede un rendering da parte del browser, il che significa che è ancora presente un INP.
Poiché il listener di eventi ha bloccato il thread principale per un secondo impedendo il rendering della pagina, l'interazione richiede comunque un secondo intero.
L'acquisizione di una registrazione del pannello Rendimento mostra l'interazione praticamente identica a quelle precedenti.
Concetti chiave
Qualsiasi codice in esecuzione in qualsiasi listener di eventi ritarderà l'interazione.
- Sono inclusi i listener registrati da script e framework diversi o il codice della libreria eseguito nei listener, ad esempio un aggiornamento dello stato che attiva il rendering di un componente.
- Non solo il tuo codice, ma anche tutti gli script di terze parti.
È un problema comune.
Infine, il fatto che il tuo codice non attivi un rendering non significa che un rendering non attenderà il completamento dei listener di eventi lenti.
7. Experiment: input delay
Che dire del codice a esecuzione prolungata al di fuori dei listener di eventi? Ad esempio:
- Se avevi un
<script>
a caricamento ritardato che bloccava la pagina in modo casuale durante il caricamento. - Una chiamata API, ad esempio
setInterval
, che blocca periodicamente la pagina?
Prova a rimuovere blockFor
dal listener di eventi e ad aggiungerlo a un setInterval()
:
Visualizza il codice completo: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
Che cosa succede?
8. Risultati dell'esperimento sul ritardo input
Visualizza il codice completo: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
La registrazione di un clic sul pulsante che si verifica durante l'esecuzione dell'attività di blocco setInterval
comporta un'interazione di lunga durata, anche se non viene eseguito alcun lavoro di blocco nell'interazione stessa.
Questi periodi di lunga durata vengono spesso chiamati attività di lunga durata.
Se passi il mouse sopra l'interazione in DevTools, vedrai che ora il tempo di interazione è attribuito principalmente al ritardo di input, non alla durata dell'elaborazione.
Tieni presente che non sempre influisce sulle interazioni. Se non fai clic durante l'esecuzione dell'attività, potresti avere fortuna. Questi starnuti "casuali" possono essere un incubo da risolvere quando causano problemi solo a volte.
Un modo per individuarli è misurare le attività lunghe (o Long Animation Frames) e il Total Blocking Time.
9. Presentazione lenta
Finora abbiamo esaminato le prestazioni di JavaScript tramite il ritardo di input o i listener di eventi, ma cos'altro influisce sul rendering del successivo paint?
Beh, aggiornando la pagina con effetti costosi.
Anche se l'aggiornamento della pagina avviene rapidamente, il browser potrebbe comunque dover lavorare sodo per eseguirne il rendering.
Nel thread principale:
- Framework UI che devono eseguire il rendering degli aggiornamenti dopo le modifiche dello stato
- Le modifiche al DOM o l'attivazione/disattivazione di molti selettori di query CSS costosi possono attivare molti stili, layout e paint.
Fuori dal thread principale:
- Utilizzo di CSS per potenziare gli effetti della GPU
- Aggiunta di immagini ad alta risoluzione molto grandi
- Utilizzo di SVG/Canvas per disegnare scene complesse
Alcuni esempi comunemente trovati sul web:
- Un sito SPA che ricostruisce l'intero DOM dopo aver fatto clic su un link, senza mettere in pausa per fornire un feedback visivo iniziale.
- Una pagina di ricerca che offre filtri di ricerca complessi con un'interfaccia utente dinamica, ma esegue listener costosi per farlo.
- Un pulsante di attivazione/disattivazione della modalità Buio che attiva lo stile/il layout per l'intera pagina
10. Experiment: presentation delay
Dispositivo requestAnimationFrame
lento
Simuliamo un lungo ritardo della presentazione utilizzando l'API requestAnimationFrame()
.
Sposta la chiamata blockFor
in un callback requestAnimationFrame
in modo che venga eseguita dopo la restituzione del listener di eventi:
Visualizza il codice completo: presentation_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Che cosa succede?
11. Risultati dell'esperimento sul ritardo presentazione
Visualizza il codice completo: presentation_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
L'interazione dura un secondo, quindi cosa è successo?
requestAnimationFrame
richiede un richiamo prima della prossima verniciatura. Poiché l'INP misura il tempo che intercorre tra l'interazione e il rendering successivo, il blockFor(1000)
in requestAnimationFrame
continua a bloccare il rendering successivo per un secondo intero.
Tuttavia, nota due cose:
- Al passaggio del mouse, vedrai che tutto il tempo di interazione viene ora speso in "ritardo di presentazione", poiché il blocco del thread principale si verifica dopo la restituzione del listener di eventi.
- La radice dell'attività del thread principale non è più l'evento di clic, ma "Animation Frame Fired".
12. Diagnostica delle interazioni
In questa pagina di test, la reattività è molto visiva, con i punteggi, i timer e l'interfaccia utente del contatore, ma quando si testa la pagina media è più sottile.
Quando le interazioni durano a lungo, non è sempre chiaro quale sia la causa. Potrebbe trattarsi di:
- Ritardo input?
- Durata dell'elaborazione dell'evento.
- Ritardo presentazione?
In qualsiasi pagina, puoi utilizzare DevTools per misurare la reattività. Per prendere l'abitudine, prova questo flusso:
- Naviga sul web come faresti normalmente.
- Tieni d'occhio il log delle interazioni nella visualizzazione delle metriche in tempo reale del riquadro Prestazioni di DevTools.
- Se noti un'interazione con prestazioni scarse, prova a ripeterla:
- Se non riesci a riprodurlo, utilizza il log delle interazioni per ottenere informazioni.
- Se riesci a riprodurlo, registra una traccia nel pannello Rendimento.
Tutti i ritardi
Prova ad aggiungere un po' di tutti questi problemi alla pagina:
Visualizza il codice completo: all_the_things.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Poi utilizza la console e il pannello del rendimento per diagnosticare i problemi.
13. Esperimento: lavoro asincrono
Poiché puoi avviare effetti non visivi all'interno delle interazioni, ad esempio effettuare richieste di rete, avviare timer o semplicemente aggiornare lo stato globale, cosa succede quando questi effetti alla fine aggiornano la pagina?
Finché è consentito il rendering del next paint dopo un'interazione, anche se il browser decide che non è necessario un nuovo aggiornamento del rendering, la misurazione dell'interazione si interrompe.
Per provare questa soluzione, continua ad aggiornare la UI dal listener di clic, ma esegui il lavoro di blocco dal timeout.
Visualizza il codice completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Che cosa succede ora?
14. Risultati dell'esperimento sul lavoro asincrono
Visualizza il codice completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
L'interazione ora è breve perché il thread principale è disponibile immediatamente dopo l'aggiornamento della UI. L'attività di blocco prolungato continua a essere eseguita, ma viene eseguita qualche tempo dopo il rendering, in modo che l'utente riceva un feedback immediato dell'interfaccia utente.
Lezione: se non riesci a rimuoverlo, almeno spostalo.
Metodi
Possiamo fare meglio di un setTimeout
fisso di 100 millisecondi? Probabilmente vogliamo ancora che il codice venga eseguito il più rapidamente possibile, altrimenti lo avremmo rimosso.
Obiettivo:
- L'interazione verrà eseguita
incrementAndUpdateUI()
. blockFor()
verrà eseguito il prima possibile, ma non bloccherà la pittura successiva.- Ciò si traduce in un comportamento prevedibile senza "timeout magici".
Alcuni modi per farlo includono:
setTimeout(0)
Promise.then()
requestAnimationFrame
requestIdleCallback
scheduler.postTask()
"requestPostAnimationFrame"
A differenza di requestAnimationFrame
da solo (che tenterà di essere eseguito prima del rendering successivo e di solito comporterà comunque un'interazione lenta), requestAnimationFrame
+ setTimeout
costituisce un semplice polyfill per requestPostAnimationFrame
, eseguendo il callback dopo il rendering successivo.
Visualizza il codice completo: raf+task.html
function afterNextPaint(callback) {
requestAnimationFrame(() => {
setTimeout(callback, 0);
});
}
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
afterNextPaint(() => {
blockFor(1000);
});
});
Per l'ergonomia, puoi persino racchiuderlo in una promessa:
Visualizza il codice completo: raf+task2.html
async function nextPaint() {
return new Promise(resolve => afterNextPaint(resolve));
}
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
await nextPaint();
blockFor(1000);
});
15. Interazioni multiple (e clic furiosi)
Spostare le attività di blocco lunghe può essere utile, ma queste attività continuano a bloccare la pagina, influendo sulle interazioni future, nonché su molte altre animazioni e aggiornamenti della pagina.
Prova di nuovo la versione della pagina con blocco asincrono (o la tua se hai creato una variante del rinvio del lavoro nell'ultimo passaggio):
Visualizza il codice completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Cosa succede se fai clic più volte rapidamente?
Traccia del rendimento
Per ogni clic, viene accodata un'attività della durata di un secondo, garantendo che il thread principale venga bloccato per un periodo di tempo considerevole.
Quando queste attività lunghe si sovrappongono ai nuovi clic in arrivo, si verificano interazioni lente anche se il listener di eventi viene restituito quasi immediatamente. Abbiamo creato la stessa situazione dell'esperimento precedente con i ritardi di input. Solo che questa volta il ritardo di input non proviene da un setInterval
, ma dal lavoro attivato da listener di eventi precedenti.
Strategie
Idealmente, vogliamo rimuovere completamente le attività lunghe.
- Rimuovi completamente il codice non necessario, in particolare gli script.
- Ottimizza il codice per evitare l'esecuzione di attività lunghe.
- Interrompi il lavoro obsoleto quando arrivano nuove interazioni.
16. Strategia 1: debounce
Una strategia classica. Quando le interazioni arrivano in rapida successione e l'elaborazione o gli effetti di rete sono costosi, ritarda l'inizio del lavoro di proposito in modo da poterlo annullare e riavviare. Questo pattern è utile per interfacce utente come i campi di completamento automatico.
- Utilizza
setTimeout
per ritardare l'avvio di operazioni costose, con un timer, ad esempio da 500 a 1000 millisecondi. - Salva l'ID timer quando lo fai.
- Se arriva una nuova interazione, annulla il timer precedente utilizzando
clearTimeout
.
Vedi il codice completo: debounce.html
let timer;
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
blockFor(1000);
}, 1000);
});
Traccia del rendimento
Nonostante i numerosi clic, viene eseguita una sola attività blockFor
, che attende un secondo intero senza clic prima di essere eseguita. Per le interazioni che si verificano a raffica, come la digitazione in un input di testo o i target degli elementi che dovrebbero ricevere più clic rapidi, questa è la strategia ideale da utilizzare per impostazione predefinita.
17. Strategia 2: interrompi il lavoro a lunga esecuzione
Esiste ancora la sfortunata possibilità che un ulteriore clic arrivi subito dopo il periodo di rimbalzo, si verifichi nel bel mezzo di questa lunga attività e diventi un'interazione molto lenta a causa del ritardo di input.
Idealmente, se un'interazione si verifica nel bel mezzo di un'attività, vogliamo interrompere il nostro lavoro in modo che le nuove interazioni vengano gestite immediatamente. Come possiamo farlo?
Esistono alcune API come isInputPending
, ma in genere è meglio dividere le attività lunghe in blocchi.
Molti setTimeout
Primo tentativo: fai qualcosa di semplice.
Visualizza il codice completo: small_tasks.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
});
});
In questo modo, il browser può pianificare ogni attività singolarmente e l'input può avere una priorità più elevata.
Siamo tornati a cinque secondi di lavoro per cinque clic, ma ogni attività di un secondo per clic è stata suddivisa in dieci attività da 100 millisecondi. Di conseguenza, anche con più interazioni che si sovrappongono a queste attività, nessuna interazione ha un ritardo di input superiore a 100 millisecondi. Il browser dà la priorità ai listener di eventi in entrata rispetto al lavoro setTimeout
e le interazioni rimangono reattive.
Questa strategia funziona particolarmente bene quando pianifichi punti di ingresso separati, ad esempio se hai una serie di funzionalità indipendenti che devi chiamare al momento del caricamento dell'applicazione. Il semplice caricamento degli script e l'esecuzione di tutto al momento della valutazione dello script potrebbero eseguire tutto in un'unica attività lunga per impostazione predefinita.
Tuttavia, questa strategia non funziona altrettanto bene per separare il codice strettamente accoppiato, come un ciclo for
che utilizza uno stato condiviso.
Ora con yield()
Tuttavia, possiamo sfruttare async
e await
moderni per aggiungere facilmente "punti di rendimento" a qualsiasi funzione JavaScript.
Ad esempio:
Visualizza il codice completo: yieldy.html
// Polyfill for scheduler.yield()
async function schedulerDotYield() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function blockInPiecesYieldy(ms) {
const ms_per_part = 10;
const parts = ms / ms_per_part;
for (let i = 0; i < parts; i++) {
await schedulerDotYield();
blockFor(ms_per_part);
}
}
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
await blockInPiecesYieldy(1000);
});
Come prima, il thread principale viene interrotto dopo un blocco di lavoro e il browser è in grado di rispondere a qualsiasi interazione in entrata, ma ora è necessario solo un await schedulerDotYield()
anziché setTimeout
separati, il che lo rende abbastanza ergonomico da usare anche nel mezzo di un ciclo for
.
Ora con AbortContoller()
Questo metodo ha funzionato, ma ogni interazione pianifica più lavoro, anche se sono arrivate nuove interazioni che potrebbero aver modificato il lavoro da svolgere.
Con la strategia di eliminazione dei rimbalzi, abbiamo annullato il timeout precedente a ogni nuova interazione. Possiamo fare qualcosa di simile qui? Un modo per farlo è utilizzare un AbortController()
:
Visualizza il codice completo: aborty.html
// Polyfill for scheduler.yield()
async function schedulerDotYield() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function blockInPiecesYieldyAborty(ms, signal) {
const parts = ms / 10;
for (let i = 0; i < parts; i++) {
// If AbortController has been asked to stop, abandon the current loop.
if (signal.aborted) return;
await schedulerDotYield();
blockFor(10);
}
}
let abortController = new AbortController();
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
abortController.abort();
abortController = new AbortController();
await blockInPiecesYieldyAborty(1000, abortController.signal);
});
Quando arriva un clic, inizia il ciclo blockInPiecesYieldyAborty
for
, che esegue il lavoro necessario cedendo periodicamente il thread principale in modo che il browser rimanga reattivo alle nuove interazioni.
Quando arriva un secondo clic, il primo ciclo viene contrassegnato come annullato con AbortController
e viene avviato un nuovo ciclo blockInPiecesYieldyAborty
. La volta successiva che il primo ciclo è pianificato per essere eseguito di nuovo, nota che signal.aborted
ora è true
e viene restituito immediatamente senza ulteriori operazioni.
18. Conclusione
La suddivisione di tutte le attività lunghe consente a un sito di rispondere a nuove interazioni. In questo modo puoi fornire rapidamente un feedback iniziale e prendere decisioni come interrompere il lavoro in corso. A volte, ciò significa pianificare i punti di ingresso come attività separate. A volte significa aggiungere punti di "rendimento" dove è più conveniente.
Va ricordato che
- L'INP misura tutte le interazioni.
- Ogni interazione viene misurata dall'input al successivo rendering, il modo in cui l'utente vede la reattività.
- Il ritardo input, la durata dell'elaborazione degli eventi e il ritardo di presentazione influenzano tutti la reattività dell'interazione.
- Con DevTools puoi misurare facilmente l'INP e le suddivisioni delle interazioni.
Strategie
- Non avere codice a esecuzione prolungata (attività lunghe) nelle tue pagine.
- Sposta il codice non necessario dai listener di eventi fino al rendering successivo.
- Assicurati che l'aggiornamento del rendering sia efficiente per il browser.