Renderización de HTML e interactividad del cliente

La renderización de HTML con JavaScript es diferente a la renderización de HTML que envía el servidor, y eso puede afectar el rendimiento. En esta guía, conocerás la diferencia y lo que puedes hacer para preservar el rendimiento de 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 algo que los navegadores hacen muy bien de forma predeterminada para los sitios web que usan la lógica de navegación integrada del navegador, a veces llamada "cargas de página tradicionales" o "navegación forzada". Estos sitios web a veces se denominan aplicaciones de varias páginas (MPAs).

Sin embargo, los desarrolladores pueden evitar los valores predeterminados del navegador para satisfacer las necesidades de su aplicación. Este es 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. La renderización 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.

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

Cómo el navegador renderiza el código HTML que proporciona 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 del navegador o haces clic en un vínculo de una MPA, se produce la siguiente serie 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 de HTML que envió el servidor y que se visualiza en el panel de rendimiento de las Herramientas para desarrolladores de Chrome. A medida que se transmite el HTML, se procesan fragmentos en varias tareas más cortas, y la renderización es incremental.
Análisis y renderización del HTML que proporciona el servidor, 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 ocurren en el navegador, el análisis de HTML se realiza dentro de las tareas. Cuando el HTML se transmite desde el servidor al navegador, este optimiza el análisis de ese HTML, ya que lo hace un poco a la vez a medida que los bits de esa transmisión llegan en fragmentos. La consecuencia es que el navegador cede al subproceso principal periódicamente después de procesar cada fragmento, lo que evita tareas largas. Esto significa que se puede realizar otro trabajo 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 el análisis y la renderización incrementales de HTML, y la entrega automática al subproceso principal de forma gratuita. No obtienes eso 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 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 el cliente propagará el área de contenido principal de una página con HTML ensamblado a partir de datos recuperados del servidor. Las navegaciones posteriores, que en este caso se denominan "navegaciones suaves", son controladas por completo por JavaScript para propagar la página con HTML nuevo.

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

Existen algunas 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 ningún 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, por varias razones, no se recomienda el uso de document.write.
Captura de pantalla del análisis de 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 partes, 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, 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 o DOM a la vez en el cliente.
  • Si se crea HTML en el cliente durante el inicio, el escáner de precarga del navegador no descubrirá los recursos a los que se hace referencia en él. Esto, sin duda, tendrá un efecto negativo en el procesamiento de imagen con contenido más grande (LCP) de una página. Si bien este no es un problema de rendimiento del entorno de ejecución (en cambio, es un problema de retraso de red en la recuperación de recursos importantes), no quieres que el LCP de tu sitio web se vea afectado por eludir esta optimización fundamental del rendimiento del navegador.

Qué puedes hacer con el 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 deficientes 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 un SPA, tus datos de campo pueden revelar interacciones responsables de un trabajo de renderización considerable.

Independientemente del motivo, estas son algunas causas potenciales que puedes explorar para solucionar el problema.

Proporcionar la mayor cantidad posible de HTML del servidor

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

Es posible que dependas de un framework de frontend para compilar tu sitio web. Si es así, asegúrate de renderizar el componente HTML 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. Siempre que sea posible, asegúrate de usar las APIs de transmisión para Node.js o otros entornos 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 HTML automáticamente en el servidor, también puede generar HTML de forma estática para las páginas que no cambian según el contexto del usuario (como la autenticación).
  • Vue también realiza la renderización 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 para que sea más fácil implementar las prácticas recomendadas.
  • Svelte renderiza HTML en el servidor de forma predeterminada, aunque si el código de tu componente necesita acceso 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 generar renderizaciones innecesarias del cliente. SvelteKit, que es para Svelte lo que Next.js es para React, incorpora muchas prácticas recomendadas en tus proyectos de Svelte tanto como sea posible, de modo que puedas evitar posibles dificultades en los 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 un SPA completo o inyecte nodos nuevos en un DOM existente como resultado de una interacción para un MPA, considera mantener esos DOM lo más pequeños posible. Esto ayudará a reducir el trabajo requerido durante la renderización del cliente para mostrar ese HTML, lo que, con suerte, ayudará a mantener el INP de tu sitio web más bajo.

Considera una arquitectura de trabajador de servicio de transmisión

Esta es una técnica avanzada, que puede no funcionar fácilmente con todos los casos de uso, pero que puede convertir tu MPA en un sitio web que parece cargarse de forma instantánea cuando los usuarios navegan de una página a la siguiente. 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 de forma correcta, no estás creando HTML en el cliente, pero la carga instantánea de partes 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 parecerse casi a un 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 trabajador de servicio de transmisión no reemplaza la lógica de navegación integrada del navegador, sino que la agrega. Para obtener más información sobre cómo lograr esto con Workbox, lee 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 afecta el rendimiento. Cuando dependes del servidor para enviar todo (o la mayor parte) del código HTML 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 presenta 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 una renderización excesiva en el sitio del cliente, asegúrate de enviar la mayor cantidad posible de HTML de tu sitio web desde el servidor siempre que sea posible, mantén los tamaños de DOM lo más pequeños posible para el 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 proporciona el navegador para el 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.

Imagen hero de Unsplash, por Maik Jonietz.