Optimiza la interacción para el siguiente procesamiento de imagen

Aprende a optimizar la interacción de tu sitio web con la siguiente pintura.

Interaction to Next Paint (INP) es una Métrica web esencial estable que evalúa la capacidad de respuesta general de una página ante las interacciones del usuario observando la latencia de todas las interacciones calificadas que ocurren durante la vida útil de un usuario a la página. El valor del INP final es la interacción más larga observada (en ocasiones, se ignoran los valores atípicos).

Para proporcionar una buena experiencia del usuario, los sitios web deben esforzarse por tener una interacción con el siguiente procesamiento de 200 milisegundos o menos. Para asegurarte de alcanzar este objetivo para la mayoría de los usuarios, un buen umbral para medir es el percentil 75 de las cargas de páginas, segmentado en dispositivos móviles y computadoras de escritorio.

Los valores de INP buenos son de 200 milisegundos o menos, los valores deficientes son superiores a 500 milisegundos y cualquier punto intermedio debe mejorar.

Según el sitio web, puede haber pocas interacciones o ninguna, como páginas que contienen principalmente texto e imágenes con pocos elementos interactivos o ninguno. O, en el caso de sitios web como editores de texto o juegos, podría haber cientos (incluso miles) de interacciones. En cualquier caso, cuando hay un INP alto, la experiencia del usuario está en riesgo.

Lleva tiempo y esfuerzo mejorar el INP, pero la recompensa es una mejor experiencia del usuario. En esta guía, se explorará una ruta para mejorar el INP.

Averigua qué está causando el INP deficiente

Para poder corregir las interacciones lentas, necesitarás datos que te indiquen si el INP de tu sitio web es deficiente o debe mejorarse. Una vez que tengas esa información, puedes avanzar al lab para empezar a diagnosticar interacciones lentas y avanzar hacia una solución.

Encuentra interacciones lentas en el campo

Lo ideal es que tu recorrido para optimizar el INP comience con los datos de campo. En el mejor de los casos, los datos de campo de un proveedor de supervisión de usuarios reales (RUM) te proporcionarán no solo el valor INP de una página, sino también datos contextuales que destacan qué interacción específica fue responsable del valor de INP, si la interacción ocurrió durante o después de la carga de la página, el tipo de interacción (clic, pulsación de teclas o toque) y otra información valiosa.

Si no dependes de un proveedor de RUM para obtener datos de campo, la guía de datos de campo de INP recomienda usar el Informe sobre la experiencia del usuario en Chrome (CrUX) a través de PageSpeed Insights para llenar los vacíos. CrUX es el conjunto de datos oficial del programa Métricas web esenciales y proporciona un resumen de alto nivel de las métricas de millones de sitios web, incluido INP. Sin embargo, CrUX a menudo no proporciona los datos contextuales que obtendrías de un proveedor de RUM para ayudarte a analizar problemas. Por este motivo, recomendamos que los sitios usen un proveedor de RUM siempre que sea posible, o bien que implementen su propia solución de RUM para complementar lo que está disponible en CrUX.

Diagnostica interacciones lentas en el lab

Lo ideal sería comenzar las pruebas en el lab una vez que tengas datos de campo que sugieran que las interacciones son lentas. Ante la ausencia de datos de campo, existen algunas estrategias para identificar interacciones lentas en el lab. Estas estrategias incluyen seguir flujos de usuarios comunes y probar interacciones a lo largo del proceso, así como interactuar con la página durante la carga (cuando el subproceso principal suele estar más ocupado) para mostrar interacciones lentas durante esa parte crucial de la experiencia del usuario.

Optimizar interacciones

Una vez que identifiques una interacción lenta y puedas reproducirla de forma manual en el lab, el siguiente paso es optimizarla. Las interacciones se pueden dividir en tres fases:

  1. El retraso de entrada, que comienza cuando el usuario inicia una interacción con la página y finaliza cuando comienzan a ejecutarse las devoluciones de llamada de eventos de la interacción.
  2. La duración del procesamiento, que consiste en el tiempo que tarda en completarse las devoluciones de llamada de eventos.
  3. El retraso de la presentación, que es el tiempo que tarda el navegador en presentar el siguiente fotograma que contiene el resultado visual de la interacción.

La suma de estas tres fases es la latencia de interacción total. Cada fase de una interacción contribuye una cantidad de tiempo a la latencia total de la interacción, por lo que es importante saber cómo puedes optimizar cada parte de la interacción para que se ejecute el menor tiempo posible.

Identifica y reduce el retraso de entradas

Cuando un usuario interactúa con una página, la primera parte de esa interacción es el retraso de entrada. Según otra actividad en la página, los retrasos en las entradas pueden tener una duración considerable. Esto podría deberse a una actividad que ocurre en el subproceso principal (quizás debido a la carga, el análisis y la compilación de secuencias de comandos), el manejo de recuperación, las funciones de cronómetro o incluso de otras interacciones que ocurren en una sucesión rápida y se superponen entre sí.

Cualquiera sea la fuente del retraso de entrada de una interacción, querrás reducir el retraso de entrada al mínimo para que las interacciones puedan comenzar a ejecutar devoluciones de llamada de eventos lo antes posible.

La relación entre la evaluación de la secuencia de comandos y las tareas largas durante el inicio

Un aspecto fundamental de la interactividad en el ciclo de vida de la página es durante el inicio. Inicialmente, mientras se carga una página, se renderiza, pero es importante recordar que el hecho de que una página se haya renderizado no significa que haya finalizado su carga. Según la cantidad de recursos que necesite una página para funcionar en su totalidad, es posible que los usuarios intenten interactuar con la página mientras se está cargando.

Algo que puede extender el retraso de la entrada de una interacción mientras se carga una página es la evaluación de la secuencia de comandos. Después de que se recupera un archivo JavaScript de la red, el navegador aún tiene trabajo por hacer antes de que JavaScript pueda ejecutarse; ese trabajo incluye analizar una secuencia de comandos para garantizar que su sintaxis sea válida, compilarla en código de bytes y, finalmente, ejecutarla.

Según el tamaño de la secuencia de comandos, este trabajo puede introducir tareas largas en el subproceso principal, lo que retrasará el navegador en la respuesta a otras interacciones del usuario. Para que tu página siga respondiendo a las entradas del usuario durante la carga, es importante que comprendas qué puedes hacer para reducir las probabilidades de que se lleven a cabo tareas largas durante la carga y, así, la página se mantenga ágil.

Optimiza las devoluciones de llamada de eventos

El retraso de entrada es solo la primera parte de lo que mide INP. También deberás asegurarte de que las devoluciones de llamada de eventos que se ejecutan en respuesta a una interacción del usuario se completen lo más rápido posible.

Cámbiate al subproceso principal con frecuencia

El mejor consejo general para optimizar las devoluciones de llamada de eventos es realizar el menor trabajo posible en ellas. Sin embargo, tu lógica de interacción puede ser compleja y es posible que solo puedas reducir marginalmente el trabajo que hacen.

Si crees que este es el caso de tu sitio web, lo siguiente que puedes intentar es dividir el trabajo en devoluciones de llamadas de eventos en tareas separadas. De esta manera, se evita que el trabajo colectivo se convierta en una tarea larga que bloquee el subproceso principal, lo que permite que otras interacciones que, de otro modo, estarían esperando en el subproceso principal para ejecutarse antes.

setTimeout es una forma de dividir las tareas, ya que la devolución de llamada que se le pasa se ejecuta en una tarea nueva. Puedes usar setTimeout solo o abstraer su uso en una función separada para obtener un rendimiento más ergonómico.

Es mejor ceder de forma indiscriminada que no ceder en lo absoluto. Sin embargo, existe una forma más sutil de ceder al subproceso principal que implica solo ceder inmediatamente después de una devolución de llamada de evento que actualiza la interfaz de usuario para que la lógica de renderización pueda ejecutarse antes.

Céntrate para permitir que el trabajo de renderización ocurra antes.

Una técnica de rendimiento más avanzada implica estructurar el código en las devoluciones de llamada de eventos para limitar lo que se ejecuta a la lógica necesaria para aplicar actualizaciones visuales al siguiente fotograma. Todo lo demás se puede aplazar a una tarea posterior. Esto no solo hace que las devoluciones de llamada sean livianas y ágiles, sino que también mejora el tiempo de renderización de las interacciones, ya que no permite que las actualizaciones visuales se bloqueen en el código de devolución de llamada de eventos.

Por ejemplo, imagina un editor de texto enriquecido que formatea el texto a medida que escribes, pero que también actualiza otros aspectos de la IU en respuesta a lo que escribiste (como el recuento de palabras, el resaltado de errores ortográficos y otros comentarios visuales importantes). Además, es posible que la aplicación también deba guardar lo que escribiste para que no hayas perdido ningún trabajo si te vas y regresas.

En este ejemplo, las siguientes cuatro situaciones deben suceder en respuesta a los caracteres que ingresó el usuario. Sin embargo, solo se debe completar el primer elemento antes de que se presente el siguiente fotograma.

  1. Actualiza el cuadro de texto con lo que escribió el usuario y aplica el formato requerido.
  2. Actualiza la parte de la IU que muestra el recuento actual de palabras.
  3. Ejecuta lógica para verificar si hay errores ortográficos.
  4. Guarda los cambios más recientes (ya sea de forma local o en una base de datos remota).

El código para hacerlo podría ser similar al siguiente:

textBox.addEventListener('input', (inputEvent) => {
  // Update the UI immediately, so the changes the user made
  // are visible as soon as the next frame is presented.
  updateTextBox(inputEvent);

  // Use `setTimeout` to defer all other work until at least the next
  // frame by queuing a task in a `requestAnimationFrame()` callback.
  requestAnimationFrame(() => {
    setTimeout(() => {
      const text = textBox.textContent;
      updateWordCount(text);
      checkSpelling(text);
      saveChanges(text);
    }, 0);
  });
});

En la siguiente visualización, se muestra cómo aplazar las actualizaciones no críticas hasta después del siguiente fotograma puede reducir la duración del procesamiento y, por lo tanto, la latencia general de la interacción.

Representación de una interacción con el teclado y las tareas posteriores en dos escenarios. En la figura superior, la tarea de renderización crítica y todas las tareas posteriores en segundo plano se ejecutan de forma síncrona hasta que llega la oportunidad de presentar un fotograma. En la figura inferior, el trabajo de renderización crítica se ejecuta primero y, luego, cede al subproceso principal para presentar un nuevo fotograma antes. Las tareas en segundo plano se ejecutan a partir de ese momento.
Haz clic en la imagen anterior para ver una versión de alta resolución.

Si bien el uso de setTimeout() dentro de una llamada a requestAnimationFrame() en el ejemplo de código anterior es sin duda un poco esotérico, es un método eficaz que funciona en todos los navegadores para garantizar que el código que no es crítico no bloquee el siguiente fotograma.

Evita la hiperpaginación de diseños

La hiperpaginación de diseños, a veces llamada diseño sincrónico forzado, es un problema de rendimiento de la renderización en el que el diseño se produce de forma síncrona. Ocurre cuando actualizas estilos en JavaScript y, luego, los lees en la misma tarea. Hay muchas propiedades en JavaScript que pueden causar la hiperpaginación del diseño.

Una visualización de la hiperpaginación de diseños, como se muestra en el panel de rendimiento de las Herramientas para desarrolladores de Chrome.
Un ejemplo de hiperpaginación de diseños, como se muestra en el panel de rendimiento de las Herramientas para desarrolladores de Chrome. Las tareas de renderización que implican la hiperpaginación de diseños se marcarán con un triángulo rojo en la esquina superior derecha de la parte de la pila de llamadas, a menudo con las etiquetas Recalculate Style o Layout.

La hiperpaginación de diseños es un cuello de botella en el rendimiento porque, al actualizar estilos y, luego, solicitar inmediatamente los valores de esos estilos en JavaScript, el navegador se ve obligado a realizar un trabajo de diseño síncrono que, de lo contrario, podría haber esperado de forma asincrónica una vez que terminan de ejecutarse las devoluciones de llamada de eventos.

Minimizar la demora en la presentación

El retraso de presentación de una interacción marca desde que las devoluciones de llamada de eventos de una interacción terminan de ejecutarse hasta el momento en que el navegador puede pintar el siguiente fotograma que muestra los cambios visuales resultantes.

Minimizar el tamaño del DOM

Cuando el DOM de una página es pequeño, el trabajo de renderización suele finalizar rápidamente. Sin embargo, cuando los DOM son muy grandes, el trabajo de representación tiende a escalar a medida que aumenta el tamaño del DOM. La relación entre el trabajo de representación y el tamaño del DOM no es lineal, pero los DOM grandes requieren más trabajo para representarse que los pequeños. Un DOM de gran tamaño resulta problemático en dos casos:

  1. Durante la representación de la página inicial, cuando un DOM grande requiere de mucho trabajo para representar el estado inicial de la página.
  2. En respuesta a una interacción del usuario, en la que un DOM grande puede hacer que las actualizaciones de la representación sean muy costosas y, por lo tanto, aumentan el tiempo que el navegador tarda en presentar el siguiente fotograma.

Ten en cuenta que, en algunos casos, los DOM grandes no se pueden reducir de forma significativa. Si bien existen enfoques que puedes adoptar para reducir el tamaño del DOM, como compactar el DOM o agregarlo al DOM durante las interacciones del usuario para mantener reducido el tamaño inicial del DOM, es posible que esas técnicas solo alcancen un límite.

Usa content-visibility para renderizar de forma diferida los elementos fuera de la pantalla

Una forma de limitar la cantidad de trabajo de renderización durante la carga de la página y el trabajo de renderización en respuesta a las interacciones del usuario es utilizar la propiedad content-visibility de CSS, que efectivamente equivale a renderizar los elementos de forma diferida a medida que se acercan al viewport. Si bien se puede practicar el uso de content-visibility de forma eficaz, vale la pena investigar si el resultado es un tiempo de renderización menor, que puede mejorar el INP de tu página.

Ten en cuenta los costos de rendimiento cuando renderices HTML con JavaScript

Cuando hay HTML, hay análisis de HTML y, una vez que el navegador termina de analizar el HTML en un DOM, debe aplicarle estilos, realizar cálculos de diseño y, posteriormente, representar ese diseño. Este es un costo inevitable, pero la forma de renderizar HTML es importante.

Cuando el servidor envía HTML, llega al navegador como un flujo. Transmisión significa que la respuesta HTML del servidor llega en fragmentos. El navegador optimiza la forma en que maneja una transmisión mediante el análisis incremental de los fragmentos de esa transmisión a medida que llegan y los representa poco a poco. Esta es una optimización de rendimiento en el sentido de que el navegador se ejecuta de forma implícita de manera periódica y automática durante la carga de la página, y se obtiene de forma gratuita.

Si bien la primera visita a cualquier sitio web siempre implicará alguna cantidad de HTML, un enfoque común comienza con un fragmento mínimo de HTML y, luego, se usa JavaScript para completar el área de contenido. Las actualizaciones posteriores de esa área de contenido también se producen como resultado de las interacciones del usuario. Por lo general, esto se denomina modelo de aplicación de una sola página (SPA). Una desventaja de este patrón es que, al procesar HTML con JavaScript en el cliente, no solo obtienes el costo del procesamiento de JavaScript para crear ese HTML, sino que también el navegador no rinderá hasta que haya terminado de analizar ese HTML y renderizarlo.

Sin embargo, es fundamental recordar que incluso los sitios web que no son SPA probablemente implican cierto nivel de procesamiento de HTML a través de JavaScript como resultado de las interacciones. Por lo general, esto es correcto, siempre y cuando no renderices grandes cantidades de HTML en el cliente, lo que puede retrasar la presentación del siguiente fotograma. Sin embargo, es importante comprender las implicaciones de rendimiento que tiene este enfoque para renderizar HTML en el navegador, y cómo puede afectar la capacidad de respuesta de tu sitio web a la entrada del usuario si renderizas gran cantidad de HTML a través de JavaScript.

Conclusión

La mejora del INP de tu sitio es un proceso iterativo. Cuando corriges una interacción lenta en el campo, hay muchas probabilidades de que, especialmente si tu sitio web proporciona mucha interactividad, comenzarás a encontrar otras interacciones lentas, y también deberás optimizarlas.

La clave para mejorar INP es la persistencia. Con el tiempo, podrás lograr que la capacidad de respuesta de tu página llegue a un lugar donde los usuarios estén satisfechos con la experiencia que les ofreces. También es muy probable que, a medida que desarrolles nuevas funciones para los usuarios, tengas que seguir el mismo proceso para optimizar las interacciones específicas para ellos. Tomará tiempo y esfuerzo, pero es tiempo y esfuerzo bien invertido.

Hero image de Unsplash, de David Pisnoy, modificada de acuerdo con la licencia de Unsplash.