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

Mariko Kosaka

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.

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

Il compositore riceve eventi di input

Figura 2: passaggio del mouse sopra l'area visibile sui livelli di pagina

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.

regione limitata a scorrimento non rapido
Figura 3: diagramma dell'input descritto nella regione a scorrimento non rapido

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.

regione a pagina intera non a scorrimento rapido
Figura 4: diagramma dell'input descritto nella regione a scorrimento non rapido che copre un'intera pagina

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

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

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

test di hit
Figura 6: il thread principale che guarda i record Paint che chiede cosa è disegnato sul punto x.y

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.

eventi non filtrati
Figura 7: eventi che interessano la sequenza temporale del frame che causa il jank della pagina

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.

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

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.

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 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

grazie

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.