Saltar al contenido

La Arquitectura: Un "Deep Dive" en el Renderizado Instantáneo

Bienvenido a la sala de máquinas.

Esta documentación no es un tutorial paso a paso, sino un análisis de la arquitectura que permite a esta aplicación lograr un rendimiento de carga casi instantáneo en recargas de página, incluso en redes 3G lentas.

El principio fundamental es simple, pero su implementación es una coreografía precisa entre tres actores: el Proceso de Build, el Service Worker (SW) y el Servidor (Nitro v2) (sí, he creado un backend en JS para vosotros, aunque mi preferencia personal es Kotlin).

El Contrato: site.json

La piedra angular del sistema es un fichero site.json generado en el proceso de build. Este fichero es el "contrato" que todos los actores entienden y respetan. En lugar de calcular offsets en tiempo de ejecución, el build analiza los ficheros HTML estáticos y pre-calcula los offsets de bytes exactos para cada "filete" de la página.

Ejemplo de site.json (simplificado):

json
{
  "pages": {
    "/": {
      "link": "<...>; rel=preload; as=script...",
      "critical": { "start": 0, "end": 34567 },
      "head": { "start": 34621, "end": 65432 }
    }
  }
}

Este manifiesto le da al servidor la información precisa para leer trozos de ficheros sin cargarlos por completo en memoria. Además, el build genera variantes comprimidas con Gzip y Brotli. El site.json contiene únicamente los offsets para el HTML original; el backend de Nitro se encargará de servir la variante comprimida correcta usando las cabeceras Content-Encoding y Vary. El HTML sin comprimir solo se sirve en la primera visita (cuando el SW aún no está activo) y cuando el propio SW precarga los assets.

El Flujo de Renderizado (SW Activo)

Cuando un usuario con el Service Worker ya instalado recarga la página, ocurre la siguiente secuencia:

  1. La Petición: El navegador pide una página.
  2. La Intercepción: El streaming-sw.ts captura la petición de navegación.
  3. Las Tareas en Paralelo: El SW lanza dos promesas simultáneamente con Promise.allSettled:
    • Cargar el "cascarón" HTML (sin datos) desde su caché local.
    • Pedir el JSON de estado a un endpoint /state-api en la red.
  4. El Ensamblaje: El SW analiza los resultados. Si la llamada al API es exitosa y devuelve un JSON, "cose" ("stitches") ese JSON dentro del stream del HTML que está sirviendo desde la caché. Si la llamada al API falla o devuelve una redirección, sirve esa respuesta del servidor directamente, respetando siempre su autoridad.

La Contraparte: El Backend de Nitro

El backend está diseñado para ser la pareja de baile perfecta de este SW. Sus dos responsabilidades clave son:

  1. Servir el HTML completo (SSR Fallback): Para la primera visita de un usuario, el servidor sirve la página completa con el estado inyectado. Para bots o para la precarga del SW, sirve la misma página pero sin estado.
  2. Servir solo el JSON: Cuando recibe una petición con la cabecera X-APP-STATE (enviada por el Service Worker), el mismo endpoint sirve únicamente los datos en formato JSON a través del /state-api.

La lógica de streaming en Nitro utiliza un único descriptor de fichero (fs.promises.open) para leer los "filetes" del HTML basándose en los offsets del site.json, asegurando la máxima eficiencia.

Liberado bajo la licencia MIT