La entrada llega al compositor
Esta es la última parte de la serie de blogs de 4 partes que analiza Chrome y cómo controla nuestro código para mostrar un sitio web. En la publicación anterior, analizamos el proceso de renderización y aprendimos sobre el compositor. En esta publicación, veremos cómo el compositor habilita una interacción fluida cuando llega la entrada del usuario.
Eventos de entrada desde el punto de vista del navegador
Cuando escuchas "eventos de entrada", es posible que solo pienses en escribir en un cuadro de texto o hacer clic con el mouse, pero desde el punto de vista del navegador, la entrada significa cualquier gesto del usuario. El desplazamiento de la rueda del mouse es un evento de entrada, y el toque o el desplazamiento del mouse también lo son.
Cuando se produce un gesto del usuario, como un toque en una pantalla, el proceso del navegador es el que recibe el gesto al principio. Sin embargo, el proceso del navegador solo sabe dónde se produjo ese gesto, ya que el proceso del renderizador controla el contenido dentro de una pestaña. Por lo tanto, el proceso del navegador envía el tipo de
evento (como touchstart
) y sus coordenadas al proceso del renderizador. El proceso del renderizador controla el evento de forma adecuada, ya que encuentra el destino del evento y ejecuta los objetos de escucha de eventos conectados.
El compositor recibe eventos de entrada
En la publicación anterior, vimos cómo el compositor podía controlar el desplazamiento de forma fluida mediante la composición de capas rasterizadas. Si no hay objetos de escucha de eventos de entrada conectados a la página, el subproceso de compositor puede crear un nuevo fotograma compuesto completamente independiente del subproceso principal. Pero, ¿qué sucede si se adjuntan algunos objetos de escucha de eventos a la página? ¿Cómo sabría el subproceso del compositor si se debe controlar el evento?
Información sobre la región desplazable no rápida
Dado que ejecutar JavaScript es la tarea del subproceso principal, cuando se compone una página, el subproceso del compositor marca una región de la página que tiene controladores de eventos adjuntos como "Región no desplazable rápidamente". Con esta información, el subproceso de compositor puede asegurarse de enviar el evento de entrada al subproceso principal si el evento ocurre en esa región. Si el evento de entrada proviene de fuera de esta región, el subproceso del compositor continúa componiendo un nuevo fotograma sin esperar al subproceso principal.
Ten cuidado cuando escribas controladores de eventos
Un patrón común de control de eventos en el desarrollo web es la delegación de eventos. Dado que los eventos se expanden, puedes adjuntar un controlador de eventos en el elemento más alto y delegar tareas según el objetivo del evento. Es posible que hayas visto o escrito código como el siguiente.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
Dado que solo debes escribir un controlador de eventos para todos los elementos, la ergonomía de este patrón de delegación de eventos es atractiva. Sin embargo, si observas este código desde el punto de vista del navegador, ahora toda la página está marcada como una región desplazable no rápida. Esto significa que, incluso si a tu aplicación no le importa la entrada de ciertas partes de la página, el subproceso del compositor debe comunicarse con el subproceso principal y esperarlo cada vez que llega un evento de entrada. Por lo tanto, se inhabilita la capacidad de desplazamiento suave del compositor.
Para evitar que esto suceda, puedes pasar opciones passive: true
en tu objeto de escucha de eventos. Esto le indica al navegador que aún quieres escuchar el evento en el subproceso principal, pero el compositor también puede seguir adelante y componer un nuevo fotograma.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
Verifica si el evento se puede cancelar
Imagina que tienes un cuadro en una página en el que quieres limitar la dirección de desplazamiento solo al desplazamiento horizontal.
El uso de la opción passive: true
en tu evento del puntero significa que el desplazamiento de la página puede ser fluido, pero es posible que el desplazamiento vertical haya comenzado cuando quieras preventDefault
para limitar la dirección del desplazamiento. Puedes verificar esto con el método 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});
Como alternativa, puedes usar una regla CSS como touch-action
para eliminar por completo el controlador de eventos.
#area {
touch-action: pan-x;
}
Cómo encontrar el objetivo del evento
Cuando el subproceso de compositor envía un evento de entrada al subproceso principal, lo primero que se ejecuta es una prueba de hit para encontrar el objetivo del evento. La prueba de hit usa datos de registros de pintura que se generaron en el proceso de renderización para averiguar qué hay debajo de las coordenadas del punto en el que ocurrió el evento.
Minimiza los envíos de eventos al subproceso principal
En la publicación anterior, analizamos cómo nuestra pantalla típica actualiza la pantalla 60 veces por segundo y cómo debemos seguir el ritmo de la cadencia para lograr una animación fluida. En el caso de la entrada, un dispositivo con pantalla táctil típico entrega eventos táctiles de 60 a 120 veces por segundo, y un mouse típico entrega eventos 100 veces por segundo. El evento de entrada tiene una fidelidad más alta que la que puede actualizar nuestra pantalla.
Si se envía un evento continuo como touchmove
al subproceso principal 120 veces por segundo, es posible que se active una cantidad excesiva de pruebas de hit y ejecución de JavaScript en comparación con la lentitud con la que se puede actualizar la pantalla.
Para minimizar las llamadas excesivas al subproceso principal, Chrome une eventos continuos (como wheel
, mousewheel
, mousemove
, pointermove
y touchmove
) y retrasa el envío hasta justo antes del siguiente requestAnimationFrame
.
Cualquier evento discreto, como keydown
, keyup
, mouseup
, mousedown
, touchstart
y touchend
, se envía de inmediato.
Usa getCoalescedEvents
para obtener eventos intrafotogramas
Para la mayoría de las aplicaciones web, los eventos fusionados deberían ser suficientes para proporcionar una buena experiencia del usuario.
Sin embargo, si compilas elementos como una aplicación de dibujo y colocas una ruta basada en coordenadas touchmove
, es posible que pierdas las coordenadas intermedias para dibujar una línea suave. En ese caso,
puedes usar el método getCoalescedEvents
en el evento del puntero para obtener información sobre esos
eventos fusionados.
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.
}
});
Próximos pasos
En esta serie, analizamos el funcionamiento interno de un navegador web. Si nunca te preguntaste por qué DevTools recomienda agregar {passive: true}
a tu controlador de eventos o por qué deberías escribir el atributo async
en tu etiqueta de secuencia de comandos, espero que esta serie te ayude a comprender por qué un navegador necesita esa información para proporcionar una experiencia web más rápida y fluida.
Cómo usar Lighthouse
Si quieres que tu código sea amigable con el navegador, pero no tienes idea de por dónde empezar, Lighthouse es una herramienta que ejecuta una auditoría de cualquier sitio web y te brinda un informe sobre lo que se está haciendo bien y lo que se debe mejorar. Leer la lista de auditorías también te da una idea de qué tipo de aspectos le interesan a un navegador.
Obtén información para medir el rendimiento
Los ajustes de rendimiento pueden variar según los diferentes sitios, por lo que es fundamental que midas el rendimiento de tu sitio y decidas qué es lo más adecuado para él. El equipo de Chrome DevTools tiene algunos instructivos sobre cómo medir el rendimiento de tu sitio.
Agrega la Política de funciones a tu sitio
Si quieres dar un paso adicional, la política de funciones es una nueva función de la plataforma web que puede servirte como protección cuando compilas tu proyecto. Si activas la política de funciones, se garantiza el comportamiento determinado de tu app y se evita que cometas errores.
Por ejemplo, si quieres asegurarte de que tu app nunca bloquee el análisis, puedes ejecutarla en la política de secuencias de comandos síncronas. Cuando se habilita sync-script: 'none'
, se impedirá que se ejecute el JavaScript que bloquea el analizador. Esto evita que tu código bloquee el analizador, y el navegador no tiene que preocuparse por pausarlo.
Conclusión
Cuando comencé a crear sitios web, casi solo me preocupaba cómo escribiría mi código y qué me ayudaría a ser más productivo. Esos aspectos son importantes, pero también debemos pensar en cómo el navegador toma el código que escribimos. Los navegadores modernos invierten y siguen invirtiendo en formas de proporcionar una mejor experiencia web a los usuarios. Si organizamos nuestro código para que sea amigable con el navegador, a su vez, mejoraremos la experiencia del usuario. Espero que te unas a mí en la búsqueda de ser amable con los navegadores.
Muchas gracias a todos los que revisaron los primeros borradores de esta serie, incluidos (sin limitaciones): Alex Russell, Paul Irish, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yasuda, Nasko Oskov y Charlie Reis.
¿Te gustó esta serie? Si tienes alguna pregunta o sugerencia para la próxima publicación, me encantaría escucharla en la sección de comentarios a continuación o en @kosamari en Twitter.