1. Introduzione
Una demo interattiva e un codelab per informazioni su Interazione con Next Paint (INP).
Prerequisiti
- Conoscenza dello sviluppo HTML e JavaScript.
- Azione consigliata: leggi la documentazione INP.
Cosa imparerai
- In che modo l'interazione delle interazioni degli utenti e la gestione di queste interazioni influiscono sulla reattività della pagina.
- Come ridurre ed eliminare i ritardi per un'esperienza utente senza problemi.
Cosa serve
- Un computer con la possibilità 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
Ottieni ed esegui 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
- Passa nella directory clonata:
cd web-vitals-codelabs/understanding-inp
- Installa dipendenze:
npm ci
- Avvia il server web:
npm run start
- Visita la pagina http://localhost:5173/understanding-inp/ nel tuo browser.
Panoramica dell'app
Nella parte superiore della pagina sono presenti un contatore Punteggio e il pulsante Aumenta. Una demo classica di reattività e reattività!
Sotto il pulsante ci sono 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 con timer in esecuzione per aiutare a visualizzare i jank.
Le voci FPS e Timer non sono affatto necessarie per misurare le interazioni. Vengono aggiunti solo per semplificare un po' la reattività della visualizzazione.
Prova
Prova a interagire con il pulsante Aumenta e guarda l'aumento del punteggio. I valori INP e Interazione cambiano a ogni incremento?
L'INP misura il tempo che intercorre tra il momento in cui l'utente interagisce e il momento in cui la pagina mostra effettivamente all'utente l'aggiornamento che ha eseguito il rendering.
3. Misurazione delle interazioni con Chrome DevTools
Apri DevTools da Altri strumenti > Menu Strumenti per sviluppatori, facendo clic con il tasto destro del mouse sulla pagina e selezionando Ispeziona oppure utilizzando una scorciatoia da tastiera.
Passa al riquadro Rendimento, che utilizzerai per misurare le interazioni.
Quindi, acquisisci un'interazione nel riquadro Rendimento.
- Premi il pulsante per registrare.
- Interagisci con la pagina (premi il pulsante Aumenta).
- 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 del secondo video scorrendo o tenendo premuto il tasto W.
Passando il mouse sopra l'interazione, puoi vedere che l'interazione è stata veloce, non ha impiegato tempo per la durata dell'elaborazione e la quantità di tempo minima in ritardo dell'input e ritardo nella presentazione, la cui lunghezza esatta dipenderà dalla velocità della macchina.
4. Listener di eventi a lunga durata
Apri il file index.js
e rimuovi il commento dalla funzione blockFor
all'interno del listener di eventi.
Visualizza 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 saranno ora notevolmente più lente.
Traccia prestazioni
Registra un'altra registrazione nel riquadro Rendimento per vedere come appare.
Quella che una volta era un'interazione breve ora richiede un secondo intero.
Quando passi il mouse sopra l'interazione, puoi notare che il tempo è quasi interamente speso in "Durata di elaborazione", ovvero il tempo impiegato per eseguire i callback del listener di eventi. Dal momento che la chiamata blockFor
che blocca è interamente all'interno del listener di eventi, il tempo passa a questo punto.
5. Esperimento: durata di elaborazione
Prova dei modi per riorganizzare il lavoro dell'ascoltatore di eventi per vedere l'effetto su INP.
Prima aggiorna l'UI
Cosa succede se inverti l'ordine delle chiamate js: aggiorni prima l'UI e poi blocchi?
Visualizza il codice completo: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Hai notato che la UI è stata visualizzata prima? L'ordine influisce sui punteggi INP?
Prova a tracciare ed esaminare l'interazione per vedere se ci sono differenze.
Listener separati
E se sposti il lavoro in un listener di eventi separato? Aggiorna l'interfaccia utente 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);
});
Come si presenta ora nel riquadro del rendimento?
Diversi tipi di evento
La maggior parte delle interazioni attiva molti tipi di eventi, dal puntatore o eventi chiave, a eventi di passaggio del mouse, focus/sfocature 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 UI
Che 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. Elaborazione dei risultati dell'esperimento sulla durata in corso...
Traccia prestazioni: prima aggiorna l'UI
Visualizza il codice completo: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Osservando la registrazione nel riquadro Rendimento dei clic sul pulsante, puoi vedere che i risultati non sono cambiati. Mentre un aggiornamento dell'interfaccia utente veniva attivato prima del codice di blocco, il browser non ha aggiornato ciò che era visualizzato sullo schermo fino al completamento del listener di eventi, il che significa che il completamento dell'interazione ha comunque richiesto poco più di un secondo.
Traccia del rendimento: listener 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 da un punto di vista funzionale. L'interazione richiede ancora un intero secondo.
Se aumenti lo zoom nell'interazione del clic, puoi notare che vengono chiamate effettivamente due funzioni diverse come risultato dell'evento click
.
Come previsto, la prima esecuzione, ovvero l'aggiornamento dell'UI, viene eseguita in modo estremamente rapido, mentre la seconda richiede un intero secondo. Tuttavia, la somma dei loro effetti si traduce nella stessa interazione lenta per l'utente finale.
Analisi del rendimento: diversi tipi di eventi
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Questi risultati sono molto simili. L'interazione è ancora di un secondo intero; l'unica differenza è che il listener click
più breve solo per gli aggiornamenti dell'interfaccia utente viene ora eseguito dopo il listener pointerup
di blocco.
Traccia del rendimento: nessun aggiornamento dell'interfaccia utente
Visualizza il codice completo: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
- Il punteggio non si aggiorna, ma la pagina sì.
- Animazioni, effetti CSS, azioni dei componenti web predefiniti (input modulo), inserimento di testo ed evidenziazione del testo continuano ad aggiornarsi.
In questo caso il pulsante passa allo stato attivo e torna allo stato attivo quando viene selezionato. Questa operazione richiede la visualizzazione 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 la visualizzazione della pagina, l'interazione richiede comunque un secondo intero.
La registrazione di un riquadro Rendimento mostra un'interazione praticamente identica a quelle precedenti.
Conclusione
Qualsiasi codice in esecuzione nel listener di eventi qualsiasi ritarda l'interazione.
- Sono inclusi i listener registrati da diversi script e codice di framework o libreria che vengono eseguiti 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 semplice fatto che il codice non attivi una colorazione non significa che non sia in attesa del completamento da parte dei listener di eventi lenti.
7. Esperimento: ritardo di input
Come funziona il codice a lunga esecuzione al di fuori dei listener di eventi? Ad esempio:
- Se hai un file
<script>
caricato in ritardo che ha bloccato in modo casuale la pagina durante il caricamento. - Una chiamata API, come
setInterval
, che blocca periodicamente la pagina?
Prova a rimuovere blockFor
dal listener di eventi e ad aggiungerlo a setInterval()
:
Visualizza il codice completo: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
Che cosa succede?
8. Inserimento dei risultati dell'esperimento sul ritardo
Visualizza il codice completo: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
La registrazione di un clic su un pulsante che si verifica mentre l'attività di blocco setInterval
era in esecuzione genera un'interazione di lunga durata, anche senza che l'interazione venga bloccata.
Questi periodi di lunga durata sono spesso chiamati attività lunghe.
Se passi il mouse sopra l'interazione in DevTools, vedrai che la durata dell'interazione è ora attribuita principalmente al ritardo dell'input, non alla durata dell'elaborazione.
Tieni presente che questo non influisce sempre sulle interazioni. Se non fai clic mentre l'attività è in esecuzione, potresti avere fortuna. Un modo "casuale" gli starnuti possono essere un incubo per il debug quando solo a volte causano problemi.
Un modo per individuarli è misurare le attività lunghe (o i frame di animazione lunghi) e il tempo di blocco totale.
9. Presentazione lenta
Finora, abbiamo esaminato le prestazioni di JavaScript, tramite il ritardo dell'input o i listener di eventi, ma quali altri fattori influisce sulla visualizzazione successiva del rendering?
Beh, aggiornare la pagina con effetti costosi!
Anche se l'aggiornamento della pagina avviene rapidamente, il browser potrebbe dover lavorare sodo per eseguirne il rendering.
Nel thread principale:
- Framework UI che devono eseguire il rendering degli aggiornamenti dopo le modifiche di stato
- Le modifiche al DOM o l'attivazione/la disattivazione di molti selettori di query CSS costosi possono attivare molti selettori di stile, layout e colorazione.
Fuori dal thread principale:
- Utilizzo di CSS per potenziare gli effetti GPU
- L'aggiunta di immagini molto grandi ad alta risoluzione
- Utilizzare SVG/Canvas per disegnare scene complesse
Alcuni esempi comunemente trovati sul web:
- Un sito SPA che ricrea l'intero DOM dopo aver fatto clic su un link, senza fermarsi per fornire un feedback visivo iniziale.
- Una pagina di ricerca che offre filtri di ricerca complessi con un'interfaccia utente dinamica, ma richiede molto per eseguire questa azione.
- Un pulsante di attivazione/disattivazione della modalità Buio che attiva lo stile/il layout per l'intera pagina
10. Esperimento: ritardo di presentazione
Dispositivo requestAnimationFrame
lento
Simuliamo un lungo ritardo nella 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 nella presentazione
Visualizza il codice completo: presentation_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
L'interazione dura ancora un secondo. Che cosa è successo?
requestAnimationFrame
richiede un callback prima della colorazione successiva. Poiché INP misura il tempo che intercorre tra l'interazione e il disegno successivo, il valore blockFor(1000)
in requestAnimationFrame
continua a bloccare il disegno successivo per un secondo intero.
Tuttavia, tieni presente due aspetti:
- Al passaggio del mouse, vedrai tutto il tempo di interazione trascorso in "ritardo nella presentazione" poiché il blocco del thread principale si verifica dopo il ritorno del listener di eventi.
- La radice dell'attività del thread principale non è più l'evento di clic, ma il "Frame di animazione attivato".
12. Diagnosi delle interazioni
In questa pagina di test, la reattività è estremamente visiva, con i punteggi, i timer e l'interfaccia utente del contatore...ma quando si testa una pagina di media, il livello di reattività è più discreto.
Quando le interazioni durano a lungo, non è sempre chiaro quale sia il colpevole. È:
- Ritardo di input?
- Durata di elaborazione dell'evento?
- Ritardo nella presentazione?
In qualsiasi pagina, puoi utilizzare DevTools per misurare la reattività. Per prendere l'abitudine, prova questa procedura:
- Naviga sul web come faresti normalmente.
- (Facoltativo) Lascia aperta la console DevTools mentre l'estensione Web Vitals registra le interazioni.
- Se noti un'interazione di scarsa qualità, prova a ripeterla:
- Se non puoi ripeterlo, utilizza i log della console per ottenere insight.
- Se puoi ripeterlo, registralo nel riquadro delle prestazioni.
Tutti i ritardi
Prova ad aggiungere alla pagina alcuni di tutti questi problemi:
Guarda il codice completo: all_the_things.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Quindi usa la console e il riquadro delle prestazioni per diagnosticare i problemi.
13. Esperimento: lavoro asincrono
Poiché è possibile 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 queste eventualmente aggiornano la pagina?
Se è possibile eseguire il rendering della verniciatura successiva dopo un'interazione, anche se il browser decide che non è necessario un nuovo aggiornamento del rendering, la misurazione dell'interazione viene interrotta.
Per fare una prova, continua ad aggiornare l'interfaccia utente dal listener di clic, ma esegui il blocco dopo il timeout.
Visualizza il codice completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Che cosa succede ora?
14. Risultati degli esperimenti di lavoro asincroni
Visualizza il codice completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Ora l'interazione è breve perché il thread principale è disponibile subito dopo l'aggiornamento dell'interfaccia utente. L'attività di blocco lunga è ancora in esecuzione, ma solo qualche tempo dopo il completamento, in modo che l'utente riceva un feedback immediato sull'interfaccia utente.
Lezione: se non riesci a rimuoverlo, fai almeno lo spostamento!
Metodi
Possiamo fare meglio di un valore fisso di 100 millisecondi setTimeout
? Probabilmente vogliamo che il codice venga eseguito il più rapidamente possibile, altrimenti dovremmo rimuoverlo.
Obiettivo:
- L'interazione verrà eseguita il giorno
incrementAndUpdateUI()
. blockFor()
verrà eseguito il prima possibile, ma non blocca la colorazione successiva.- Questo si traduce in un comportamento prevedibile senza "timeout magici".
Ecco alcuni modi per raggiungere questo obiettivo:
setTimeout(0)
Promise.then()
requestAnimationFrame
requestIdleCallback
scheduler.postTask()
"requestPostAnimationFrame"
A differenza di requestAnimationFrame
da solo (che cercherà di essere eseguito prima della colorazione successiva e di solito genererà un'interazione lenta), requestAnimationFrame
+ setTimeout
crea un semplice polyfill per requestPostAnimationFrame
, con l'esecuzione del callback dopo la colorazione successiva.
Visualizza il codice completo: raf+task.html
function afterNextPaint(callback) {
requestAnimationFrame(() => {
setTimeout(callback, 0);
});
}
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
afterNextPaint(() => {
blockFor(1000);
});
});
Per quanto riguarda l'ergonomia, hai anche una promessa:
Consulta 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 irregolari)
Spostarsi su una pagina di blocco può essere d'aiuto, ma queste attività lunghe bloccano comunque la pagina, influenzando le interazioni future, così come molte altre animazioni e aggiornamenti della pagina.
Prova di nuovo la versione del blocco asincrono al lavoro della pagina (o la tua se hai trovato una variante personale sul rinvio del lavoro nell'ultimo passaggio):
Visualizza il codice completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Che cosa succede se fai clic più volte velocemente?
Traccia prestazioni
Per ogni clic, viene messa in coda un'attività di un secondo, che garantisce che il thread principale sia bloccato per una quantità di tempo considerevole.
Quando queste attività lunghe si sovrappongono a nuovi clic in arrivo, le interazioni sono lente anche se il listener di eventi stesso restituisce quasi immediatamente. Abbiamo creato la stessa situazione dell'esperimento precedente con ritardi nell'input. Solo questa volta, il ritardo di input non proviene da un setInterval
, ma da un lavoro attivato da listener di eventi precedenti.
Strategie
Idealmente, vogliamo rimuovere completamente le attività lunghe.
- Rimuovi completamente il codice non necessario, soprattutto gli script.
- Ottimizza il codice per evitare di eseguire attività lunghe.
- Interrompi il lavoro inattivo quando arrivano nuove interazioni.
16. Strategia 1: debounce
Una strategia classica. Ogni volta che le interazioni arrivano in rapida successione e gli effetti di elaborazione o di rete sono costosi, ritarda l'avvio del lavoro intenzionale per poter annullare e riavviare il processo. Questo pattern è utile per le interfacce utente, come i campi di completamento automatico.
- Usa
setTimeout
per ritardare l'avvio di lavori costosi, con un timer, ad esempio da 500 a 1000 millisecondi. - In questo caso, salva l'ID del timer.
- Se arriva una nuova interazione, annulla il timer precedente utilizzando
clearTimeout
.
Visualizza il codice completo: debounce.html
let timer;
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
blockFor(1000);
}, 1000);
});
Traccia prestazioni
Nonostante i numerosi clic, rimane in esecuzione una sola attività blockFor
, che prima di eseguire l'esecuzione è che attende che non ci siano stati clic per un secondo intero. Per le interazioni a raffica, come la digitazione di un input di testo o target di elementi che dovrebbero ricevere più clic rapidi, questa è una strategia ideale da utilizzare per impostazione predefinita.
17. Strategia 2: interrompere il lavoro a lungo termine
C'è comunque la sfortuna possibilità che un altro clic venga attivato subito dopo che il periodo di debounce è trascorso, che venga atterrato nel mezzo di un'attività lunga e che diventi un'interazione molto lenta a causa del ritardo dell'input.
Idealmente, se nel corso dell'attività si verifica un'interazione, è consigliabile mettere in pausa il lavoro svolto in modo che tutte le nuove interazioni vengano gestite immediatamente. Come possiamo farlo?
Esistono alcune API come isInputPending
, ma in genere è meglio suddividere le attività lunghe in blocchi.
Molti setTimeout
s
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);
});
});
Ciò avviene consentendo al browser di pianificare ogni attività singolarmente e l'input può avere una priorità maggiore.
Torniamo a cinque secondi completi di lavoro per cinque clic, ma ogni attività da un secondo per clic è stata suddivisa in dieci attività da 100 millisecondi. Di conseguenza, anche se più interazioni si sovrappongono alle attività in questione, nessuna interazione ha un ritardo di input superiore a 100 millisecondi. Il browser assegna la priorità ai listener di eventi in arrivo rispetto al lavoro setTimeout
e le interazioni rimangono adattabili.
Questa strategia funziona particolarmente bene quando si pianificano punti di ingresso separati, ad esempio se si dispone di una serie di funzionalità indipendenti da chiamare al momento di caricamento dell'applicazione. Per impostazione predefinita, il semplice caricamento degli script e l'esecuzione di tutti gli elementi al momento della valutazione può consentire di eseguire tutto in un'attività molto lunga e molto lunga.
Tuttavia, questa strategia non è efficace per scomporre il codice ad alto accoppiamento, come nel caso di un loop for
che utilizza lo stato condiviso.
Ora con yield()
Tuttavia, possiamo sfruttare i moderni async
e await
per aggiungere facilmente "punti di rendimento" a qualsiasi funzione JavaScript.
Ad esempio:
Visualizza il codice completo: rendery.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 in precedenza, il thread principale viene restituito dopo un tratto di lavoro e il browser è in grado di rispondere a qualsiasi interazione in entrata, ma ora tutto ciò che serve è un await schedulerDotYield()
invece di setTimeout
separati, il che lo rende sufficientemente ergonomico da essere utilizzato anche nel bel mezzo di un loop for
.
Ora con AbortContoller()
Ciò ha funzionato, ma ogni interazione pianifica più lavoro, anche se sono arrivate nuove interazioni e potrebbero aver modificato il lavoro da svolgere.
Con la strategia di antirimbalzo, abbiamo annullato il timeout precedente a ogni nuova interazione. Possiamo fare qualcosa di simile? Un modo per farlo è utilizzare una 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, viene avviato il loop blockInPiecesYieldyAborty
for
che esegue le operazioni necessarie, generando periodicamente il thread principale, in modo che il browser rimanga reattivo alle nuove interazioni.
Quando arriva un secondo clic, il primo loop viene contrassegnato come annullato con AbortController
e viene avviato un nuovo ciclo blockInPiecesYieldyAborty
: la prossima volta che viene pianificata l'esecuzione del primo loop, il sistema nota che signal.aborted
è ora true
e ritorna immediatamente senza ulteriori operazioni.
18. Conclusione
Suddividere tutte le attività lunghe permette a un sito di reagire alle nuove interazioni. In questo modo puoi fornire rapidamente il tuo feedback iniziale e prendere decisioni, come interrompere la procedura in corso. A volte questo significa pianificare i punti di ingresso come attività separate. A volte, questo significa aggiungere "rendimento" in punti diversi, ove opportuno.
Va ricordato che
- INP misura tutte le interazioni.
- Ogni interazione viene misurata dall'input alla visualizzazione successiva, ovvero il modo in cui l'utente vede la reattività.
- Il ritardo dell'input, la durata dell'elaborazione degli eventi e il ritardo nella presentazione influiscono tutti sulla reattività dell'interazione.
- Puoi misurare facilmente l'INP e le suddivisioni delle interazioni con DevTools.
Strategie
- Non avere codice di lunga durata (attività lunghe) nelle tue pagine.
- Sposta il codice inutile dai listener di eventi fino al successivo colorazione.
- Assicurati che l'aggiornamento del rendering sia efficace per il browser.