Renderizar HTML con JavaScript es diferente a renderizar el HTML que envía el servidor, y eso puede afectar el rendimiento. En esta guía, encontrarás la diferencia y descubre lo que puedes hacer para preservar el rendimiento de la renderización de tu sitio web, especialmente en lo que respecta a las interacciones.
El análisis y la representación de HTML es algo que los navegadores hacen muy bien de forma predeterminada para los sitios web que utilizan la lógica de navegación integrada del navegador (a veces denominadas "cargas tradicionales de páginas"). o "navegación difícil". 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 el caso de los sitios web que utilizan 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 Interacción con la siguiente pintura (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 envía el servidor al navegador frente a crearlo en el cliente con JavaScript, y cómo este último puede generar una latencia alta de interacción en momentos cruciales.
Cómo renderiza el navegador el HTML que proporciona el servidor
El patrón de navegación utilizado en las cargas de página 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 en una MPA, ocurrirá la siguiente serie de eventos:
- El navegador envía una solicitud de navegación para la URL proporcionada.
- El servidor responde con HTML en fragmentos.
El último paso es clave. Además, es una de las optimizaciones de rendimiento más fundamentales en el intercambio de servidor y 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.
![Una captura de pantalla del análisis del HTML que envió el servidor, visualizada en el panel de rendimiento de las Herramientas para desarrolladores de Chrome. A medida que el código HTML ingresa, se procesan fragmentos de él en varias tareas más cortas, y la representación es gradual.](https://web.dev/static/articles/client-side-rendering-of-html-and-interactivity/image/a-screenshot-parsing-ht-f2443fe1b1888.png?authuser=5&hl=es)
Como la mayoría de las cosas que suceden en el navegador, el análisis de HTML se realiza dentro de las tareas. Cuando el HTML se transmite del servidor al navegador, el navegador optimiza el análisis de ese HTML al hacerlo 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 las tareas largas. Esto significa que se pueden llevar a cabo otras tareas mientras se analiza 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 un análisis y una renderización incremental del HTML, además del procesamiento automático del subproceso principal, de forma gratuita. Eso no ocurre con la renderización del cliente.
Cómo el navegador renderiza 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 utilizan el patrón SPA. Este enfoque suele implicar que el servidor proporciona una carga útil inicial mínima de HTML, pero luego el cliente completa el área de contenido principal de una página con HTML ensamblado a partir de los datos obtenidos del servidor. Navegaciones posteriores, a veces denominadas "navegaciones flexibles" en este caso, sean manejadas completamente por JavaScript para completar la página con nuevo HTML.
La renderización del cliente también puede ocurrir en casos que no son SPA en casos más limitados en los que HTML se agrega dinámicamente al DOM a través de JavaScript.
Existen algunas formas comunes de crear HTML o agregar elementos al DOM por medio de JavaScript:
- La propiedad
innerHTML
te permite configurar el contenido de un elemento existente a través de una cadena, que el navegador analiza en DOM. - El método
document.createElement
te permite crear elementos nuevos para agregarlos al DOM sin usar ningún análisis HTML del navegador. - El método
document.write
te permite escribir HTML en el documento (y el navegador lo analiza, al igual que en el enfoque n.o 1). Sin embargo, por varias razones, no se recomienda el uso dedocument.write
.
![Captura de pantalla del análisis de HTML renderizado a través de JavaScript visualizado en el panel de rendimiento de Herramientas para desarrolladores de Chrome. El trabajo se lleva a cabo en una sola tarea larga que bloquea el subproceso principal.](https://web.dev/static/articles/client-side-rendering-of-html-and-interactivity/image/a-screenshot-parsing-ht-7b89c1ab03511.png?authuser=5&hl=es)
Las consecuencias de crear HTML/DOM a través del código 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 del cliente no se fragmentan automáticamente, lo que puede dar como resultado tareas largas que bloqueen el subproceso principal. Esto significa que el INP de tu página puede verse afectado de manera negativa si creas demasiados elementos de HTML/DOM al mismo tiempo en el cliente.
- Si se crea HTML en el cliente durante el inicio, el análisis de precarga del navegador no detectará los recursos a los que se hace referencia en él. Sin duda, esto tendrá un efecto negativo en el Largest Contentful Paint (LCP) de una página. Si bien este no es un problema de rendimiento del entorno de ejecución (es un problema de demora en la red para recuperar 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 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 deficientes en los datos de tu campo, es posible que te preguntes si la renderización del cliente está relacionada con el problema. Por ejemplo, si tu sitio web es una SPA, los datos de tu campo pueden revelar interacciones responsables de un trabajo de renderización considerable.
Cualquiera sea la causa, aquí hay algunas causas potenciales que puedes explorar para ayudar a que todo vuelva a la normalidad.
Proporciona la mayor cantidad de código HTML del servidor posible.
Como se mencionó antes, el navegador maneja el HTML del servidor de un modo muy eficaz de forma predeterminada. Rompe el análisis y la renderización del HTML de una manera que evita tareas largas y optimiza la cantidad de tiempo total del subproceso principal. Esto genera un Tiempo de bloqueo total (TBT) más bajo, y el TBT está muy correlacionado con el INP.
Es posible que dependas de un framework de frontend para crear tu sitio web. Si es así, asegúrate de que el componente HTML esté renderizando en el servidor. Esto limitará la cantidad de renderización inicial del cliente que requerirá tu sitio web, lo que debería dar como resultado una mejor experiencia.
- Para React, te recomendamos usar la API del 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, además de 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 entornos de ejecución de JavaScript, de modo 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 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, de modo que las prácticas recomendadas sean más fáciles de implementar.
- Svelte renderiza HTML en el servidor de forma predeterminada. Sin embargo, si el código de tu componente necesita acceder a espacios de nombres exclusivos del navegador (por ejemplo,
window
), es posible que no puedas renderizar el HTML del componente en el servidor. Explora enfoques alternativos siempre que sea posible para no causar renderizaciones innecesarias del cliente. SvelteKit, que es Svelte como Next.js es React, incorpora muchas prácticas recomendadas a tus proyectos de Svelte en la medida de lo posible para que puedas evitar posibles errores en proyectos que solo usan Svelte.
Limita la cantidad de nodos del DOM creados en el cliente
Cuando los DOM son grandes, el procesamiento requerido para representarlos tiende a aumentar. Ya sea que tu sitio web sea una SPA completa o esté inyectando nuevos nodos en un DOM existente como resultado de una interacción con una MPA, considera mantener esos DOM lo más pequeño posible. Esto te ayudará a reducir el trabajo requerido durante la renderización del lado del cliente para mostrar ese HTML, lo que podría contribuir a reducir el INP de tu sitio web.
Considera una arquitectura de service worker de transmisión
Esta es una técnica avanzada que puede no funcionar fácilmente con todos los casos de uso, pero es una que puede convertir tu MPA en un sitio web que parece que se carga instantáneamente 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.
Si utilizas esta técnica con éxito, no crearás HTML en el cliente, pero la carga instantánea de partes parciales del contenido de la caché dará la impresión de que tu sitio se carga rápidamente. Los sitios web que utilizan este enfoque pueden parecer casi 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 agrega. Si quieres obtener más información para lograr esto con Workbox, consulta Aplicaciones más rápidas de varias páginas con transmisiones.
Conclusión
La forma en que tu sitio web recibe y renderiza el código HTML afecta el rendimiento. Cuando confías en que el servidor envía todo (o la mayor parte) del código HTML necesario para que tu sitio web funcione, obtienes muchos beneficios gratuitos: análisis y renderización incrementales, y cedido automático al subproceso principal para evitar tareas largas.
El procesamiento 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 completamente la totalidad del tiempo. Para reducir las posibles tareas largas que pueden producirse por la renderización excesiva del 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 HTML que se deba procesar en el cliente y considera arquitecturas alternativas para acelerar la entrega de HTML al cliente, a la vez que aprovechas el análisis y la renderización gradual que el navegador proporciona para 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 LCP, TBT y, posiblemente, incluso tu TTFB en algunos casos.
Hero image de Unsplash, de Maik Jonietz.