Renderización de HTML e interactividad del cliente

Renderizar HTML con JavaScript es diferente a renderizar HTML que envía el servidor, y eso puede afectar el rendimiento. En esta guía, obtén información sobre la diferencia y lo que puedes hacer para preservar el rendimiento de la renderización de tu sitio web, en especial en lo que respecta a las interacciones.

El análisis y la renderización de HTML son tareas que los navegadores realizan muy bien de forma predeterminada para los sitios web que usan la lógica de navegación integrada del navegador, a veces denominada "cargas de página tradicionales" o "navegaciones difíciles". Estos sitios web a veces se denominan aplicaciones de varias páginas (MPA).

Sin embargo, los desarrolladores pueden evitar los valores predeterminados del navegador para satisfacer las necesidades de sus aplicaciones. Este es, sin duda, el caso de los sitios web que usan el patrón de aplicación de una sola página (SPA), que crea de forma dinámica grandes partes del HTML/DOM en el cliente con JavaScript. El renderizado del cliente es el nombre de este patrón de diseño, y puede tener efectos en la Interaction to Next Paint (INP) de tu sitio web si el trabajo involucrado es excesivo.

En esta guía, se te ayudará a sopesar la diferencia entre usar el código HTML que envía el servidor al navegador y crearlo en el cliente con JavaScript, y cómo este último puede generar una latencia de interacción alta en momentos cruciales.

Cómo el navegador renderiza el código HTML proporcionado por el servidor

El patrón de navegación que se usa en las cargas de páginas tradicionales implica recibir HTML del servidor en cada navegación. Si ingresas una URL en la barra de direcciones de tu navegador o haces clic en un vínculo de una MPA, se produce la siguiente secuencia de eventos:

  1. El navegador envía una solicitud de navegación para la URL proporcionada.
  2. El servidor responde con HTML en fragmentos.

El último paso de estos es clave. También es una de las optimizaciones de rendimiento más fundamentales en el intercambio entre el servidor y el navegador, y se conoce como transmisión. Si el servidor puede comenzar a enviar HTML lo antes posible y el navegador no espera a que llegue toda la respuesta, el navegador puede procesar el HTML en fragmentos a medida que llega.

Captura de pantalla del análisis del código HTML enviado por el servidor visualizado en el panel de rendimiento de las Herramientas para desarrolladores de Chrome. A medida que se transmite el HTML, sus fragmentos se procesan en varias tareas más cortas, y la renderización es incremental.
Análisis y renderización del código HTML proporcionado por el servidor, tal como se visualiza en el panel de rendimiento de las Herramientas para desarrolladores de Chrome. Las tareas involucradas en el análisis y la renderización de HTML se dividen en fragmentos.

Al igual que la mayoría de las acciones que se realizan en el navegador, el análisis de HTML se produce dentro de las tareas. Cuando el HTML se transmite desde el servidor al navegador, este optimiza el análisis de ese HTML haciéndolo de a poco a medida que llegan fragmentos de esa transmisión. La consecuencia es que el navegador cede el control al subproceso principal periódicamente después de procesar cada fragmento, lo que evita las tareas largas. Esto significa que se pueden realizar otros trabajos mientras se analiza el código HTML, incluido el trabajo de renderización incremental necesario para presentar una página al usuario, así como el procesamiento de las interacciones del usuario que pueden ocurrir durante el período de inicio crucial de la página. Este enfoque se traduce en una mejor puntuación de Interaction to Next Paint (INP) para la página.

¿La conclusión? Cuando transmites HTML desde el servidor, obtienes análisis y renderización incrementales de HTML, y la cesión automática al subproceso principal de forma gratuita. Esto no sucede con el procesamiento del cliente.

Cómo el navegador renderiza el código HTML proporcionado por JavaScript

Si bien cada solicitud de navegación a una página requiere que el servidor proporcione una cierta cantidad de HTML, algunos sitios web usarán el patrón de SPA. Este enfoque suele implicar que el servidor proporciona una carga útil inicial mínima de HTML, pero luego el cliente completará el área de contenido principal de una página con HTML ensamblado a partir de los datos recuperados del servidor. Las navegaciones posteriores, a veces denominadas "navegaciones suaves" en este caso, se controlan por completo con JavaScript para completar la página con HTML nuevo.

El procesamiento del cliente también puede ocurrir en sitios que no son SPA en casos más limitados en los que se agrega HTML de forma dinámica al DOM a través de JavaScript.

Existen varias formas comunes de crear HTML o agregarlo al DOM a través de JavaScript:

  1. La propiedad innerHTML te permite establecer el contenido en un elemento existente a través de una cadena, que el navegador analiza en el DOM.
  2. El método document.createElement te permite crear elementos nuevos para agregarlos al DOM sin usar el análisis de HTML del navegador.
  3. El método document.write te permite escribir HTML en el documento (y el navegador lo analiza, al igual que en el enfoque 1). Sin embargo, debido a varios motivos, no se recomienda el uso de document.write.
Captura de pantalla del análisis del código HTML renderizado a través de JavaScript visualizado en el panel de rendimiento de las Herramientas para desarrolladores de Chrome. El trabajo se realiza en una sola tarea larga que bloquea el subproceso principal.
Análisis y renderización de HTML a través de JavaScript en el cliente, como se visualiza en el panel de rendimiento de las Herramientas para desarrolladores de Chrome. Las tareas involucradas en el análisis y la renderización no se dividen en fragmentos, lo que genera una tarea larga que bloquea el subproceso principal.

Las consecuencias de crear HTML/DOM a través de JavaScript del cliente pueden ser significativas:

  • A diferencia del HTML que transmite el servidor en respuesta a una solicitud de navegación, las tareas de JavaScript en el cliente no se dividen automáticamente en fragmentos, lo que puede generar tareas largas que bloqueen el subproceso principal. Esto significa que el INP de tu página puede verse afectado negativamente si creas demasiado HTML/DOM a la vez en el cliente.
  • Si se crea HTML en el cliente durante el inicio, el navegador no descubrirá los recursos a los que se hace referencia en él con el escáner de precarga. Esto sin duda tendrá un efecto negativo en el Largest Contentful Paint (LCP) de una página. Si bien este no es un problema de rendimiento en el tiempo de ejecución (sino un problema de retraso de red en la recuperación de recursos importantes), no querrás que el LCP de tu sitio web se vea afectado por eludir esta optimización fundamental del rendimiento del navegador.

Qué puedes hacer con respecto al impacto en el rendimiento de la renderización del cliente

Si tu sitio web depende en gran medida de la renderización del cliente y observaste valores de INP bajos en tus datos de campo, es posible que te preguntes si la renderización del cliente tiene algo que ver con el problema. Por ejemplo, si tu sitio web es una SPA, tus datos de campo pueden revelar interacciones responsables de un trabajo de renderización considerable.

Cualquiera sea la causa, aquí tienes algunas posibles causas que puedes explorar para volver a encarrilar las cosas.

Proporciona la mayor cantidad posible de HTML desde el servidor

Como se mencionó anteriormente, el navegador controla el HTML del servidor de forma muy eficiente de forma predeterminada. Dividirá el análisis y la renderización de HTML de una manera que evite las tareas largas y optimice la cantidad de tiempo total del subproceso principal. Esto genera un Total Blocking Time (TBT) más bajo, y el TBT está altamente correlacionado con el INP.

Es posible que uses un framework de frontend para compilar tu sitio web. Si es así, asegúrate de renderizar el HTML del componente en el servidor. Esto limitará la cantidad de renderización inicial del cliente que requerirá tu sitio web y debería generar una mejor experiencia.

  • En el caso de React, te recomendamos que uses la API de DOM del servidor para renderizar HTML en el servidor. Sin embargo, ten en cuenta que el método tradicional de renderización del servidor usa un enfoque síncrono, lo que puede generar un tiempo hasta el primer byte (TTFB) más largo, así como métricas posteriores, como el primer procesamiento de imagen con contenido (FCP) y el LCP. Cuando sea posible, asegúrate de usar las APIs de transmisión para Node.js o otros tiempos de ejecución de JavaScript para que el servidor pueda comenzar a transmitir HTML al navegador lo antes posible. Next.js, un framework basado en React, proporciona muchas prácticas recomendadas de forma predeterminada. Además de renderizar automáticamente el código HTML en el servidor, también puede generar de forma estática el código HTML para las páginas que no cambian según el contexto del usuario (como la autenticación).
  • Vue también realiza el procesamiento del cliente de forma predeterminada. Sin embargo, al igual que React, Vue también puede renderizar el HTML de tu componente en el servidor. Aprovecha estas APIs del servidor siempre que sea posible o considera una abstracción de nivel superior para tu proyecto de Vue y, así, facilitar la implementación de las prácticas recomendadas.
  • De forma predeterminada, Svelte renderiza HTML en el servidor, aunque, si el código de tu componente necesita acceder a espacios de nombres exclusivos del navegador (window, por ejemplo), es posible que no puedas renderizar el HTML de ese componente en el servidor. Explora enfoques alternativos siempre que sea posible para no provocar una renderización innecesaria del cliente. SvelteKit, que es para Svelte lo que Next.js es para React, incorpora tantas prácticas recomendadas como sea posible en tus proyectos de Svelte, de modo que puedas evitar posibles problemas en proyectos que solo usan Svelte.

Limita la cantidad de nodos DOM creados en el cliente

Cuando los DOM son grandes, el procesamiento necesario para renderizarlos tiende a aumentar. Ya sea que tu sitio web sea una SPA completa o que inserte nodos nuevos en un DOM existente como resultado de una interacción para una MPA, considera mantener esos DOM lo más pequeños posible. Esto ayudará a reducir el trabajo necesario durante la renderización del cliente para mostrar ese código HTML, lo que, con suerte, ayudará a mantener un INP más bajo en tu sitio web.

Considera una arquitectura de Service Worker de transmisión

Esta es una técnica avanzada, que tal vez no funcione fácilmente con todos los casos de uso, pero puede convertir tu MPA en un sitio web que se cargue de forma instantánea cuando los usuarios navegan de una página a otra. Puedes usar un service worker para almacenar en caché previamente las partes estáticas de tu sitio web en CacheStorage mientras usas la API de ReadableStream para recuperar el resto del código HTML de una página desde el servidor.

Cuando usas esta técnica correctamente, no creas HTML en el cliente, pero la carga instantánea de parciales de contenido desde la caché dará la impresión de que tu sitio se carga rápidamente. Los sitios web que usan este enfoque pueden parecer casi como una SPA, pero sin las desventajas de la renderización del cliente. También reduce la cantidad de HTML que solicitas al servidor.

En resumen, una arquitectura de Service Worker de transmisión no reemplaza la lógica de navegación integrada del navegador, sino que la complementa. Para obtener más información sobre cómo lograr esto con Workbox, consulta Aplicaciones de varias páginas más rápidas con transmisiones.

Conclusión

La forma en que tu sitio web recibe y renderiza el código HTML tiene un impacto en el rendimiento. Cuando dependes del servidor para enviar todo el código HTML (o la mayor parte) necesario para que funcione tu sitio web, obtienes mucho de forma gratuita: análisis y renderización incrementales, y cesión automática al subproceso principal para evitar tareas largas.

La renderización de HTML del cliente introduce varios problemas de rendimiento potenciales que se pueden evitar en muchos casos. Sin embargo, debido a los requisitos de cada sitio web individual, no se puede evitar por completo el 100% de las veces. Para mitigar las posibles tareas largas que pueden resultar de la renderización excesiva del cliente, asegúrate de enviar la mayor cantidad posible del código HTML de tu sitio web desde el servidor siempre que sea posible, mantén los tamaños del DOM lo más pequeños posible para el código HTML que se debe renderizar en el cliente y considera arquitecturas alternativas para acelerar la entrega de HTML al cliente y, al mismo tiempo, aprovechar el análisis y la renderización incrementales que el navegador proporciona para el código HTML cargado desde el servidor.

Si logras que la renderización del cliente de tu sitio web sea lo más mínima posible, mejorarás no solo el INP de tu sitio web, sino también otras métricas, como el LCP, el TBT y, posiblemente, incluso el TTFB en algunos casos.

Hero image de Unsplash, de Maik Jonietz.