Entrada al compositor
Esta es la última de la serie de blogs de 4 partes que analiza el interior de Chrome y investiga cómo maneja nuestro código para mostrar un sitio web. En la publicación anterior, vimos 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 entra el 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 en un 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 es un evento de entrada.
Cuando se produce un gesto del usuario, como tocar en una pantalla, el proceso del navegador es el que recibe el gesto en primer lugar. Sin embargo, el proceso del navegador solo reconoce dónde ocurrió el 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 procesador controla el evento de manera adecuada. Para ello, encuentra el destino del evento y ejecuta objetos de escucha de eventos adjuntos.
El compositor recibe eventos de entrada
En la publicación anterior, observamos cómo el compositor puede controlar el desplazamiento sin problemas mediante la composición de capas rasterizadas. Si no se adjuntan objetos de escucha de eventos de entrada a la página, el subproceso compositor puede crear un nuevo marco compuesto por completo independiente del subproceso principal. Pero, ¿qué sucedería si se adjuntaran algunos objetos de escucha de eventos a la página? ¿Cómo averigua el subproceso compositor si se debe controlar el evento?
Información sobre las regiones en las que no es posible desplazarse rápidamente
Dado que ejecutar JavaScript es el trabajo 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 rápida de desplazamiento". Con esta información, el subproceso 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 compositor continúa con la composición del nuevo marco 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. Desde el cuadro de eventos, puedes adjuntar un controlador de eventos en el elemento superior y delegar tareas según el destino del evento. Es posible que hayas visto o escrito un código como el que se muestra a continuación.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
Dado que solo necesitas 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 no rápida de desplazamiento. Esto significa que, incluso si a la aplicación no le interesan las entradas de ciertas partes de la página, el subproceso compositor debe comunicarse con el subproceso principal y esperarla cada vez que ingresa un evento de entrada. Por lo tanto, se pierde la capacidad de desplazamiento suave del compositor.
Para evitar que esto suceda, puedes pasar las opciones de passive: true
en el objeto de escucha
de eventos. Esto indica al navegador que aún deseas escuchar el evento en el subproceso principal, pero el compositor también puede crear un nuevo fotograma.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
Verifica si se puede cancelar el evento
Imagina que en una página tienes un cuadro en el que quieres limitar la dirección de desplazamiento solo para que sea horizontal.
Si usas la opción passive: true
en tu evento de puntero, 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 de 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 de CSS como touch-action
para eliminar por completo el controlador de eventos.
#area {
touch-action: pan-x;
}
Encuentra el objetivo del evento
Cuando el subproceso compositor envía un evento de entrada al subproceso principal, lo primero que se debe ejecutar es una prueba de posicionamiento para encontrar el destino del evento. La prueba de posicionamiento usa datos de registros de pintura que se generaron en el proceso de renderización para averiguar qué hay debajo de las coordenadas de puntos en las que se produjo el evento.
Cómo minimizar 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 mantener la cadencia para lograr una animación fluida. En cuanto a la entrada, un dispositivo de pantalla táctil típico envía un evento táctil de 60 a 120 veces por segundo, y un mouse típico envía eventos de 100 veces por segundo. El evento de entrada tiene mayor fidelidad que la que puede actualizarse la pantalla.
Si se envió un evento continuo como touchmove
al subproceso principal 120 veces por segundo, es posible que active una cantidad excesiva de pruebas de posicionamiento y ejecuciones 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 fusiona eventos continuos (como wheel
, mousewheel
, mousemove
, pointermove
, touchmove
) y retrasa el envío hasta justo antes del siguiente requestAnimationFrame
.
Todos los eventos discretos, como keydown
, keyup
, mouseup
, mousedown
, touchstart
y touchend
, se envían de inmediato.
Usa getCoalescedEvents
para obtener eventos dentro del fotograma
En la mayoría de las aplicaciones web, los eventos combinados deberían ser suficientes para proporcionar una buena experiencia del usuario.
Sin embargo, si compilas elementos como dibujar una aplicación y colocar un trazado basado en coordenadas touchmove
, es posible que pierdas datos intermedios entre las coordenadas 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 unidos.
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 pensaste en por qué
DevTools recomienda agregar {passive: true}
en tu controlador de eventos o por qué podrías escribir el atributo async
en tu etiqueta de secuencia de comandos, esperamos 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.
Usar Lighthouse
Si deseas que tu código se adapte al navegador, pero no sabes por dónde comenzar, Lighthouse es una herramienta que ejecuta auditorías de cualquier sitio web y te brinda un informe sobre lo que se hace bien y lo que se debe mejorar. Leer la lista de auditorías también te da una idea de los aspectos que le interesan a un navegador.
Aprende a medir el rendimiento
Estos ajustes pueden variar según el sitio, por lo que es fundamental que lo midas y decidas qué opción se adapta mejor a él. El equipo de las Herramientas para desarrolladores de Chrome 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 función nueva de plataforma web que puede servirte como barandilla cuando compilas tu proyecto. Activar
la política de funciones garantiza el cierto comportamiento de tu app y evita que cometas errores.
Por ejemplo, si deseas asegurarte de que tu app nunca bloquee el análisis, puedes ejecutarla en una política de secuencias de comandos síncronas. Cuando se habilite sync-script: 'none'
, no se ejecutará JavaScript que bloquea los analizadores. De esta manera, se evita que cualquier código bloquee el analizador, y el navegador no tiene que preocuparse por detener el analizador.
Conclusión
Cuando comencé a crear sitios web, casi solo me importaba cómo escribiría mi código y qué me ayudaría a ser más productiva. Esto es importante, pero también debemos pensar en cómo el navegador toma el código que escribimos. Los navegadores modernos han invertido, y siguen invirtiendo en maneras de proporcionar una mejor experiencia web para los usuarios. Ser agradable con el navegador y organizar nuestro código, a su vez, mejora la experiencia del usuario. ¡Espero que nos acompañes en la misión de ser amable con los navegadores!
Queremos agradecer a todos los que revisaron los primeros borradores de esta serie, incluidos, sin limitaciones, Alex Russell, Paul Ireland, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yaasklieo}.
¿Te gustó esta serie? Si tienes preguntas o sugerencias para la próxima publicación, comunícate con nosotros en la sección de comentarios que aparece a continuación o en @kosamari en Twitter.