Uno sguardo all'interno del browser web moderno (parte 4)

Mariko Kosaka

L'input arriva al Compositor

Questo è l'ultimo post della serie di 4 articoli che esaminano Chrome e il modo in cui gestisce il nostro codice per visualizzare un sito web. Nel post precedente abbiamo esaminato la procedura di rendering e abbiamo appreso informazioni sul compositore. In questo post, esamineremo in che modo il compositore consente un'interazione fluida quando viene ricevuto l'input dell'utente.

Eventi di input dal punto di vista del browser

Quando senti "eventi di input", potresti pensare solo alla digitazione in una casella di testo o al clic del mouse, ma dal punto di vista del browser, l'input indica qualsiasi gesto dell'utente. La rotella del mouse è un evento di input e anche il tocco o il passaggio del mouse è un evento di input.

Quando si verifica un gesto dell'utente, ad esempio un tocco sullo schermo, inizialmente è il processo del browser a ricevere il gesto. Tuttavia, il processo del browser è a conoscenza solo del punto in cui si è verificato il gesto, poiché i contenuti all'interno di una scheda sono gestiti dal processo del renderer. Di conseguenza, il processo del browser invia il tipo di evento (come touchstart) e le relative coordinate al processo del renderer. Il processo del renderer gestisce l'evento in modo appropriato individuando la destinazione dell'evento ed eseguendo i listener di eventi collegati.

evento di input
Figura 1: evento di input indirizzato tramite il processo del browser al processo del renderer

Il compositore riceve eventi di input

Figura 2: area visibile con passaggio del mouse sopra i livelli di pagina

Nel post precedente abbiamo visto come il compositore potesse gestire facilmente lo scorrimento componendo gli strati rasterizzati. Se alla pagina non sono associati ascoltatori di eventi di input, il thread del compositore può creare un nuovo frame composito completamente indipendente dal thread principale. Ma cosa succede se alcuni ascoltatori di eventi sono associati alla pagina? In che modo il thread del compositore scopre se l'evento deve essere gestito?

Informazioni sulle regioni non scorrevoli velocemente

Poiché l'esecuzione di JavaScript è il compito del thread principale, quando una pagina viene composta, il thread del compositore contrassegnato come "Regione non scorrevole veloce" una regione della pagina a cui sono associati gestori eventi. Avendo 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 a comporre il nuovo frame senza attendere il thread principale.

regione con scorrimento limitato e non veloce
Figura 3: diagramma dell'input descritto per la regione non scorrevole rapida

Fai attenzione quando scrivi i gestori di eventi

Un pattern di gestione degli eventi comune nello sviluppo web è la delega degli eventi. Dal fumetto degli eventi, puoi collegare un gestore di eventi all'elemento più in alto e delegare le attività in base al target dell'evento. Potresti aver visto o scritto codice come quello riportato di seguito.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault();
    }
});

Poiché devi scrivere un solo gestore eventi per tutti gli elementi, l'ergonomia di questo pattern di delega degli eventi è interessante. Tuttavia, se esamini questo codice dal punto di vista del browser, ora l'intera pagina è contrassegnata come area non scorrevole velocemente. Ciò significa che anche se la tua applicazione non si preoccupa dell'input da determinate parti della pagina, il thread del compositore deve comunicare con il thread principale e attendere ogni volta che viene ricevuto un evento di input. Di conseguenza, la scorrevolezza del compositore viene meno.

regione non scorrevole a pagina intera
Figura 4: diagramma dell'input descritto per la regione non scorrevole veloce che copre un'intera pagina

Per evitare che ciò accada, puoi passare le opzioni passive: true nell'ascoltatore di eventi. Questo suggerisce al browser che vuoi ancora ascoltare l'evento nel thread principale, ma anche il compositore può procedere e comporre un nuovo frame.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});

Verificare se l'evento è annullabile

scorrimento pagina
Figura 5: una pagina web con parte della pagina fissata allo scorrimento orizzontale

Immagina di avere una casella in una pagina per la quale vuoi limitare la direzione di scorrimento solo allo scorrimento orizzontale.

L'utilizzo dell'opzione passive: true nell'evento del cursore significa che lo scorrimento della pagina può essere fluido, ma lo scorrimento verticale potrebbe essere iniziato nel momento in cui vuoi preventDefault per limitare la direzione di scorrimento. Puoi eseguire il controllo 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 eventi.

#area {
  touch-action: pan-x;
}

Trovare il target evento

test di corrispondenza
Figura 6: il thread principale esamina i record di pittura chiedendo cosa viene disegnato nel punto x.y

Quando il thread del compositore invia un evento di input al thread principale, la prima cosa da eseguire è un test di corrispondenza per trovare il target dell'evento. Il test di hit utilizza i dati dei record di pittura generati durante il processo di rendering per scoprire cosa si trova sotto le coordinate del punto in cui si è verificato l'evento.

Ridurre al minimo l'invio di eventi al thread principale

Nel post precedente abbiamo parlato di come il nostro display tipico aggiorna lo schermo 60 volte al secondo e di come dobbiamo stare al passo con la cadenza per un'animazione fluida. Per l'input, un tipico dispositivo con touchscreen genera eventi touch 60-120 volte al secondo, mentre un mouse genera eventi 100 volte al secondo. L'evento di input ha una fedeltà superiore a 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 test di hit ed esecuzione di JavaScript rispetto alla velocità di aggiornamento della schermata.

Eventi non filtrati
Figura 7: gli eventi che invadono la sequenza temporale del frame causano un comportamento discontinuo della pagina

Per ridurre al minimo le chiamate eccessive al thread principale, Chrome unisce gli eventi continui (ad esempio wheel, mousewheel, mousemove, pointermove, touchmove) e ritarda l'invio fino al momento immediatamente precedente al successivo requestAnimationFrame.

Eventi uniti
Figura 8: stessa sequenza temporale di prima, ma l'evento viene unito e ritardato

Eventuali eventi discreti come keydown, keyup, mouseup, mousedown, touchstart e touchend vengono inviati immediatamente.

Utilizza getCoalescedEvents per ottenere gli eventi intraframe

Per la maggior parte delle applicazioni web, gli eventi uniti dovrebbero essere sufficienti per offrire un'esperienza utente ottimale. Tuttavia, se stai creando elementi come disegnare un'applicazione e inserire un percorso in base alle coordinate di touchmove, potresti perdere le coordinate tra un'applicazione e l'altra per tracciare una linea smussata. In questo caso, puoi utilizzare il metodo getCoalescedEvents nell'evento del cursore per ottenere informazioni su questi eventi uniti.

getCoalescedEvents
Figura 9: percorso del gesto tocco fluido a sinistra, percorso limitato unito a destra
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 esaminato il funzionamento interno di un browser web. Se non hai mai pensato al motivo per cui DevTools consiglia di aggiungere {passive: true} nel gestore di eventi o perché potresti scrivere l'attributo async nel tag script, mi auguro che questa serie faccia chiarezza sul motivo per cui un browser ha bisogno di queste informazioni per fornire un'esperienza web più rapida e fluida.

Usa Lighthouse

Se vuoi che il tuo codice sia ottimizzato per il browser, ma non sai da dove iniziare, Lighthouse è uno strumento che esegue il controllo di qualsiasi sito web e fornisce un report su cosa è stato fatto correttamente e cosa deve essere migliorato. La lettura dell'elenco dei controlli ti dà anche un'idea di quali sono le preoccupazioni di un browser.

Scopri come misurare il rendimento

Le modifiche al rendimento possono variare a seconda del sito, quindi è fondamentale misurare il rendimento del tuo sito e decidere quale risponde meglio al tuo sito. Il team di Chrome DevTools ha pubblicato alcuni tutorial su come misurare il rendimento del sito.

Aggiungere le norme relative alle funzionalità al sito

Se vuoi fare un ulteriore passo avanti, le norme relative alle funzionalità sono una nuova funzionalità della piattaforma web che può essere un riferimento per te durante la creazione del progetto. L'attivazione delle norme relative alle funzionalità garantisce il comportamento certo della tua app ed evita che tu possa commettere errori. Ad esempio, se vuoi assicurarti che la tua app non blocchi mai l'analisi, puoi eseguire l'app utilizzando un criterio per gli script sincroni. Quando la funzionalità sync-script: 'none' è abilitata, non sarà possibile eseguire il codice JavaScript che blocca l'analizzatore sintattico. In questo modo, il codice non blocca il parser e il browser non deve preoccuparsi di mettere in pausa il parser.

Conclusione

grazie

Quando ho iniziato a creare siti web, mi interessavo quasi solo di come scrivere il codice e di cosa mi avrebbe aiutato a essere più produttivo. Questi aspetti sono importanti, ma dobbiamo anche pensare a come il browser interpreta il codice che scriviamo. I browser moderni hanno investito e continuano a investire in soluzioni per offrire agli utenti un'esperienza web migliore. Essere gentili con il browser organizzando il nostro codice, a sua volta, migliora la tua esperienza utente. Spero che vorrai unirti a me nella missione di essere gentile con i browser.

Un enorme grazie a tutti coloro che hanno esaminato le prime bozze di questa serie, tra cui (a titolo esemplificativo): Alex Russell, Paul Irish, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yasuda, Nasko Oskov e Charlie Reis.

Ti è piaciuta questa serie? Se hai domande o suggerimenti per i post futuri, non esitare a contattarmi nella sezione dei commenti qui sotto o su @kosamari su Twitter.