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 (ad esempio touchstart) e le relative coordinate al processo del renderer. Il processo di rendering gestisce l'evento in modo appropriato trovando il target dell'evento ed eseguendo gli ascoltatori di eventi collegati.

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

Il compositore riceve eventi di input

Figura 2: il viewport passa il mouse sopra i livelli di pagina

Nel post precedente abbiamo esaminato come il compositore potesse gestire lo scorrimento in modo fluido mediante la composizione di livelli 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 sulla regione non scorrevole veloce

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. Con queste informazioni, il thread del compositore può assicurarsi di inviare l'evento di input al thread principale se 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.

area 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. Poiché gli eventi si propagano, puoi collegare un gestore 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 regione non scorrevole rapida. Ciò significa che anche se la tua applicazione non si occupa 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 a scorrimento lento completa della pagina
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 comunque ascoltare l'evento nel thread principale, ma il compositore può procedere e comporre anche il nuovo frame.

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

Controlla se l'evento è annullabile

scorrimento della 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 evento unito e ritardato

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, ad esempio, un'applicazione di disegno e inserisci un percorso in base alle coordinatetouchmove, potresti perdere le coordinate intermedie per disegnare una linea uniforme. 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} al gestore eventi o perché potresti scrivere l'attributo async nel tag script, spero che questa serie chiarisca perché un browser ha bisogno di queste informazioni per offrire un'esperienza web più rapida e fluida.

Utilizzare 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 in base ai siti, pertanto è fondamentale misurare il rendimento del tuo sito e decidere cosa è più adatto per il 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 eseguirla in base alle norme relative agli script sincroni. Quando sync-script: 'none' è attivato, l'esecuzione di JavaScript che blocca il parser verrà impedita. 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 tu voglia 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.