Optimiza la interacción para el siguiente procesamiento de imagen

Descubre cómo optimizar la interacción de tu sitio web con Next Paint.

Interaction to Next Paint (INP) es una métrica de 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 la visita del usuario a una página. El valor de INP final es la interacción más larga observada (a veces ignorando valores atípicos).

Para proporcionar una buena experiencia del usuario, los sitios web deben esforzarse por tener una interacción con la siguiente pintura 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 entre dispositivos móviles y computadoras de escritorio.

Los buenos valores de INP son de 200 milisegundos o menos, los valores malos son mayores a 500 milisegundos y cualquier valor intermedio debe mejorarse.

Según el sitio web, es posible que haya pocas interacciones o ninguna. Por ejemplo, páginas que incluyan principalmente imágenes y texto con pocos o ningún elemento interactivo. O, en el caso de sitios web como editores de texto o juegos, podría haber cientos y hasta miles de interacciones. En cualquier caso, cuando el INP sea alto, la experiencia del usuario estará en riesgo.

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

Averiguar qué causa un INP deficiente

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

Detecta interacciones lentas en el campo

Idealmente, tu recorrido en la optimización del INP comenzará 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 de INP de una página, sino también datos contextuales que destacan qué interacción específica fue responsable del valor de INP en sí, si la interacción ocurrió durante o después de la carga de la página, el tipo de interacción (clic, tecla o toque) y otra información valiosa.

Si no dependes de un proveedor de RUM para obtener datos de los campos, en la guía de datos de campos de INP te recomendamos que uses 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 de Métricas web esenciales y proporciona un resumen de alto nivel de las métricas para millones de sitios web, incluido INP. Sin embargo, a menudo CrUX no proporciona los datos contextuales que obtendrías de un proveedor de RUM para ayudarte a analizar los problemas. Por ello, recomendamos que los sitios usen un proveedor de RUM siempre que sea posible, o bien implementen su propia solución de RUM para complementar lo que está disponible en CrUX.

Diagnostica interacciones lentas en el lab

Idealmente, empieza 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 las 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 activo) para mostrar interacciones lentas durante esa parte fundamental de la experiencia del usuario.

Optimizar interacciones

Una vez que identifiques una interacción lenta y puedas reproducirla manualmente 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 para la interacción.
  2. La duración del procesamiento, que consiste en el tiempo que tardan en ejecutarse las devoluciones de llamada de eventos hasta completarse.
  3. La demora en 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 total de las interacciones. Cada fase de una interacción contribuye en cierta 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.

Cómo identificar y reducir el retraso de entrada

Cuando un usuario interactúa con una página, la primera parte de esa interacción es el retraso de entrada. Según otras actividades de la página, las demoras en la entrada pueden tener una duración considerable. Esto podría deberse a la actividad que tiene lugar en el subproceso principal (quizás debido a la carga, el análisis y la compilación de las secuencias de comandos), al manejo de la recuperación, a las funciones del temporizador o incluso a otras interacciones que ocurren rápidamente y se superponen entre sí.

Independientemente de la fuente del retraso de entrada de una interacción, te recomendamos reducirlo 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 crítico de la interactividad en el ciclo de vida de la página es durante el inicio. Cuando se cargue una página, se renderizará inicialmente, pero es importante recordar que el hecho de que se haya renderizado la página no significa que haya terminado de cargarse. Según la cantidad de recursos que requiera una página para funcionar completamente, es posible que los usuarios intenten interactuar con la página mientras se está cargando.

Algo que puede extender el retraso de 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 obtiene un archivo JavaScript de la red, el navegador aún tiene trabajo que 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 una secuencia de comandos, este trabajo puede introducir tareas largas en el subproceso principal, lo que retrasará que el navegador responda a otras interacciones del usuario. Para que tu página siga siendo responsiva a la entrada del usuario mientras se carga, es importante que comprendas lo que puedes hacer para reducir la probabilidad de que se realicen tareas largas durante esta carga y que 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 el 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 puedan completarse lo más rápido posible.

cede el paso al subproceso principal con frecuencia

El mejor consejo general para optimizar las devoluciones de llamadas de eventos es hacer el menor trabajo posible en ellas. Sin embargo, tu lógica de interacción puede ser compleja y solo podrías reducir marginalmente el trabajo que realizan.

Si consideras que este es el caso de tu sitio web, lo siguiente que puedes probar es dividir el trabajo en devoluciones de llamada 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 otras interacciones que, de lo contrario, estarían esperando que el subproceso principal se ejecuten 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 por sí solo o abstraer su uso en una función independiente para obtener un rendimiento más ergonómico.

Es mejor rendir de manera indiscriminada que no rendir en absoluto. Sin embargo, hay una forma más matizada de ceder al subproceso principal, que implica solo generar el rendimiento inmediatamente después de una devolución de llamada de evento que actualice la interfaz de usuario para que la lógica de renderización pueda ejecutarse antes.

cede para permitir que el trabajo de renderización ocurra antes.

Una técnica de generación más avanzada implica estructurar el código en las devoluciones de llamada de eventos a fin de limitar lo que se ejecuta solo a la lógica necesaria para aplicar actualizaciones visuales en el siguiente fotograma. Todo lo demás se puede diferir a una tarea posterior. Esto no solo mantiene las devoluciones de llamada 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, la función para destacar 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, si te vas y regresas, no hayas perdido ningún trabajo.

En este ejemplo, las siguientes cuatro cosas deben suceder en respuesta a los caracteres que escribe el usuario. Sin embargo, solo se debe hacer 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 la 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 hacer esto 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 diferir las actualizaciones no críticas hasta después del siguiente fotograma puede reducir la duración del procesamiento y, por lo tanto, la latencia de interacción general.

Representación de una interacción con el teclado y las tareas posteriores en dos situaciones. En la figura superior, la tarea crítica para la renderización 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, se ejecuta primero el trabajo esencial para la renderización y, luego, se cede al subproceso principal para presentar un nuevo fotograma antes. Las tareas en segundo plano se ejecutan a partir de entonces.
Haz clic en la imagen anterior para ver una versión en alta resolución.

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

Cómo evitar 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. Además, 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.
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 una hiperpaginación del diseño se identificarán con un triángulo rojo en la esquina superior derecha de la parte de la pila de llamadas, que suele etiquetarse como Volver a calcular el estilo o Diseño.

La hiperpaginación de diseños es un cuello de botella de rendimiento, ya que cuando se actualizan estilos y luego se solicitan inmediatamente los valores de esos estilos en JavaScript, el navegador se ve obligado a realizar un trabajo de diseño síncrono que, de otro modo, podría haber esperado para realizarlo de forma asíncrona más adelante, después de que hayan terminado de ejecutarse las devoluciones de llamada de eventos.

Minimizar retraso de presentación

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

Minimiza 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 renderización tiende a escalar a medida que aumenta el tamaño del DOM. La relación entre el trabajo de renderización y el tamaño del DOM no es lineal, pero los DOM grandes requieren más trabajo para la renderización que los DOM pequeños. Un DOM grande es problemático en dos casos:

  1. Durante la renderización inicial de la página, donde un DOM grande requiere mucho trabajo para renderizar el estado inicial de la página.
  2. En respuesta a una interacción del usuario, un DOM de gran tamaño puede hacer que las actualizaciones de renderización sean muy costosas y, por lo tanto, aumenten el tiempo que tarda el navegador 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 tu DOM o agregarlo al DOM durante las interacciones del usuario para mantener pequeño el tamaño inicial del DOM, es posible que esas técnicas no sean tan útiles.

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

Una forma de limitar el trabajo de renderización durante la carga de la página y el de renderización en respuesta a las interacciones del usuario es recurrir a la propiedad content-visibility de CSS, que equivale a renderizar elementos de forma diferida a medida que se acercan al viewport. Si bien es necesario practicar content-visibility para usarla de manera eficaz, vale la pena investigar si el resultado es un tiempo de renderización más bajo que pueda mejorar el INP de la página.

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

Cuando hay HTML, se analiza HTML y, una vez que el navegador termina de analizar HTML en un DOM, debe aplicarle estilos, realizar cálculos de diseño y, luego, renderizar ese diseño. Este es un costo inevitable, pero cómo procesas HTML es importante.

Cuando el servidor envía HTML, llega al navegador como una transmisión. La transmisión significa que la respuesta HTML del servidor llega en fragmentos. Para optimizar la forma en que controla una transmisión, el navegador analiza de forma incremental los fragmentos de esa transmisión a medida que llegan y los renderiza poco a poco. Se trata de una optimización de rendimiento que el navegador realiza de manera implícita de forma periódica y automática durante la carga de la página, y puedes obtenerla de forma gratuita.

Si bien la primera visita a cualquier sitio web implicará siempre algo de código HTML, un enfoque común comienza con un fragmento inicial 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 de los usuarios. Por lo general, esto se denomina modelo de aplicación de una sola página (SPA). Una desventaja de este patrón es que, cuando renderizas 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 lo genera hasta que termine de analizarlo y renderizarlo.

Sin embargo, es fundamental recordar que incluso los sitios web que no son SPA probablemente implican una cierta cantidad 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 ante la entrada del usuario si renderizas mucho 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, es muy probable que, especialmente si tu sitio web proporciona mucha interactividad, comiences a encontrar otras interacciones lentas y también deberás optimizarlas.

La clave para mejorar el INP es la persistencia. Con el tiempo, podrás lograr que la capacidad de respuesta de tu página llegue a los usuarios que estén satisfechos con la experiencia que les brindas. También es bueno que, a medida que desarrollas nuevas funciones para los usuarios, tengas que pasar por el mismo proceso de optimización de las interacciones específicas para ellos. Llevará tiempo y esfuerzo, pero requiere de ese tiempo y esfuerzo.

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