L'input sarà in arrivo nel Compositor
Questo è l'ultimo di una serie di quattro blog dedicata a esaminare Chrome per capire come gestisce il nostro codice per visualizzare un sito web. Nel post precedente abbiamo esaminato il processo di rendering e abbiamo scoperto il compositore. In questo post vedremo in che modo il compositore consente un'interazione fluida quando riceve l'input dell'utente.
Inserisci gli eventi dal punto di vista del browser
Quando senti "eventi di input", ti viene in mente solo la digitazione nella casella di testo o il clic del mouse, ma dal punto di vista del browser, per input si intende qualsiasi gesto dell'utente. Lo scorrimento della rotellina del mouse è un evento di input, mentre il tocco o il mouseover è un evento di input.
Quando si verifica un gesto dell'utente come un tocco sullo schermo, il processo del browser è quello che riceve inizialmente il gesto. Tuttavia, il processo del browser è consapevole solo di dove si è verificato il gesto, poiché i contenuti all'interno di una scheda sono gestiti dal processo di rendering. Di conseguenza, il processo del browser invia il tipo
di evento (ad esempio touchstart
) e le relative coordinate al processo di rendering. Il processo del renderer gestisce l'evento in modo appropriato individuando la destinazione dell'evento ed eseguendo i listener di eventi collegati.
Il compositore riceve eventi di input
Nel post precedente, abbiamo visto in che modo il compositore poteva gestire lo scorrimento componendo livelli rasterizzati. Se alla pagina non è collegato alcun listener di eventi di input, il thread Compositor può creare un nuovo frame composito completamente indipendente dal thread principale. Ma cosa succede se alcuni ascoltatori di eventi fossero allegati alla pagina? In che modo il thread del compositore può scoprire se l'evento deve essere gestito?
Informazioni sulla regione a scorrimento non rapido
Poiché l'esecuzione di JavaScript è il job del thread principale, quando una pagina è composita, il thread del compositore contrassegna una regione della pagina con gestori di eventi collegati come "Non-Fast Scrollable Region". Con queste informazioni, il thread del compositore può assicurarsi di inviare l'evento di input al thread principale se l'evento si verifica in quella regione. Se l'evento di input proviene dall'esterno di questa regione, il thread del compositore continua la composizione del nuovo frame senza attendere il thread principale.
Fai attenzione quando scrivi gestori di eventi
Un modello di gestione degli eventi comune nello sviluppo web è la delega degli eventi. Poiché gli eventi vengono visualizzati come fumetto, puoi collegare un gestore di eventi all'elemento più in alto e delegare le attività in base alla destinazione dell'evento. Potresti aver visto o scritto un codice come quello riportato di seguito.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
Poiché è sufficiente scrivere un solo gestore di eventi per tutti gli elementi, l'ergonomia di questo modello di delega degli eventi è interessante. Tuttavia, se guardi il codice dal punto di vista del browser, ora l'intera pagina viene contrassegnata come area a scorrimento non rapido. Questo significa che anche se all'applicazione non sono interessati gli input da determinate parti della pagina, il thread del compositore deve comunicare con il thread principale e attendere che venga ricevuto ogni volta che arriva un evento di input. In questo modo, la capacità di scorrimento fluido del compositore viene annullata.
Per evitare che ciò accada, puoi trasmettere le opzioni passive: true
all'ascoltatore di eventi. Questo suggerisce al browser che vuoi ancora ascoltare l'evento nel thread principale,
ma il compositore può anche comporre un nuovo frame.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
Controllare se l'evento può essere annullato
Immagina di avere una casella in una pagina che vuoi limitare alla direzione di scorrimento solo in orizzontale.
Se utilizzi l'opzione passive: true
nell'evento puntatore, lo scorrimento della pagina può essere uniforme, ma lo scorrimento verticale potrebbe essere iniziato nel momento in cui vuoi preventDefault
per limitare la direzione di scorrimento. Puoi effettuare un confronto utilizzando il metodo event.cancelable
.
document.body.addEventListener('pointermove', event => {
if (event.cancelable) {
event.preventDefault(); // block the native scroll
/*
* do what you want the application to do here
*/
}
}, {passive: true});
In alternativa, puoi utilizzare una regola CSS come touch-action
per eliminare completamente il gestore di eventi.
#area {
touch-action: pan-x;
}
Trovare il target dell'evento
Quando il thread del compositore invia un evento di input al thread principale, la prima cosa da eseguire è un test di hit per trovare la destinazione dell'evento. L'hit test utilizza i dati dei record di colorazione generati durante il processo di rendering per scoprire cosa si trova sotto le coordinate del punto in cui si è verificato l'evento.
Riduzione al minimo dell'invio di eventi al thread principale
Nel post precedente, abbiamo parlato di come il nostro display standard aggiorna lo schermo 60 volte al secondo e di come dobbiamo stare al passo con la frequenza per un'animazione fluida. Per l'input, un tipico dispositivo touchscreen invia eventi touch 60-120 volte al secondo e un mouse tipico invia eventi 100 volte al secondo. L'evento di input ha una fedeltà più alta di quella che può essere aggiornata dallo schermo.
Se un evento continuo come touchmove
viene inviato al thread principale 120 volte al secondo, potrebbe attivare un numero eccessivo di hit test ed esecuzione di JavaScript rispetto alla lentezza di aggiornamento della schermata.
Per ridurre al minimo le chiamate eccessive al thread principale, Chrome unisce gli eventi continui (come
wheel
, mousewheel
, mousemove
, pointermove
, touchmove
) e ritarda l'invio fino
al giorno requestAnimationFrame
successivo.
Tutti gli eventi separati, come keydown
, keyup
, mouseup
, mousedown
, touchstart
e touchend
, vengono inviati immediatamente.
Usa getCoalescedEvents
per recuperare eventi all'interno del frame
Per la maggior parte delle applicazioni web, gli eventi combinati dovrebbero essere sufficienti per offrire una buona esperienza utente.
Tuttavia, se stai costruendo elementi come il disegno di un'applicazione e l'inserimento di un percorso basato sulle coordinate touchmove
, potresti perdere le coordinate tra una linea e l'altra per tracciare una linea uniforme. In questo caso, puoi utilizzare il metodo getCoalescedEvents
nell'evento puntatore per ottenere informazioni su questi eventi combinati.
window.addEventListener('pointermove', event => {
const events = event.getCoalescedEvents();
for (let event of events) {
const x = event.pageX;
const y = event.pageY;
// draw a line using x and y coordinates.
}
});
Passaggi successivi
In questa serie abbiamo trattato i meccanismi interni di un browser web. Se non hai mai pensato al motivo per cui DevTools ti consiglia di aggiungere {passive: true}
al gestore di eventi o al motivo per cui potresti scrivere l'attributo async
nel tag script, spero che questa serie abbia fatto luce sul motivo per cui un browser ha bisogno di queste informazioni per offrire un'esperienza web più veloce e fluida.
Utilizza Lighthouse
Se vuoi che il codice sia in linea con il browser ma non sai da dove iniziare, Lighthouse è uno strumento che esegue il controllo di qualsiasi sito web e ti fornisce un report su ciò che viene fatto correttamente e su ciò che richiede miglioramenti. Leggendo l'elenco dei controlli puoi anche farti un'idea degli interessi di un browser.
Scopri come misurare il rendimento
Le modifiche alle prestazioni possono variare a seconda del sito, pertanto è fondamentale che misuri le prestazioni del tuo sito e decidi quale opzione è più adatta. Il team di Chrome DevTools offre alcuni tutorial su come misurare le prestazioni del sito.
Aggiungi norme sulle funzionalità al tuo sito
Se vuoi fare un ulteriore passaggio, Feature Policy è una nuova funzionalità della piattaforma web che può essere un sistema di protezione durante la creazione del progetto. L'attivazione dei criteri relativi alle funzionalità garantisce un determinato comportamento della tua app e ti impedisce di commettere errori.
Ad esempio, se vuoi assicurarti che la tua app non blocchi mai l'analisi, puoi eseguirla in base al criterio per gli script sincroni. Se sync-script: 'none'
è abilitato, l'esecuzione di JavaScript che blocca l'analizzatore sintattico verrà bloccata. Questo impedisce che il codice blocchi il parser e il browser non deve preoccuparsi di metterlo in pausa.
Conclusione
Quando ho iniziato a creare siti web, mi interessava quasi solo come scriveressi il codice e che cosa avrebbe contribuito ad aumentare la mia produttività. Questi dati sono importanti, ma dovremmo anche pensare a come il browser prende il codice che scriviamo. I browser moderni investono e continuano a investire per offrire una migliore esperienza web agli utenti. Essere corretti con il browser organizzando il nostro codice migliora a sua volta l'esperienza utente. Spero che ti unirai a me nel tentativo di essere gentile con i browser.
Un enorme ringraziamento a tutti coloro che hanno esaminato le prime bozze di questa serie, inclusi, a titolo esemplificativo, Alex Russell, Paul Ireland, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yasmani1.
Ti è piaciuta questa serie? Per eventuali domande o suggerimenti per il prossimo post, vorrei ricevere il tuo feedback nella sezione dei commenti qui sotto o @kosamari su Twitter.