Angelitedocs ESEN

Inicio › Llevarlo a producción

SSR y precompilación

Renderizado en servidor con hydrate() y compilación de plantillas en el build para enviar solo el runtime.

Renderizado en servidor#

@duvanjamid/angelite/tools/ssr.mjs renderiza un componente en Node (con jsdom, que ya es una dependencia de build): no hay una segunda implementación que mantener, la salida es la que construiría el navegador, con la misma sanitización de URL y el mismo escapado. Las peticiones se encolan, así que dos renderizados a la vez no se pisan.

// servidor (Node)
import { renderPage } from '@duvanjamid/angelite/tools/ssr.mjs';
import { provideRouter } from '@duvanjamid/angelite';

const html = await renderPage(App, {
  shell: '<!doctype html><body><!--app--><!--state--><script type="module" src="/client.js"></script></body>',
  url: req.url,                                              // ruta y query
  setup: () => provideRouter(rutas, { mode: 'history' }),     // se ejecuta con el DOM ya disponible
  ready: async app => { await app.usuarios.settled; },        // espera a tus datos
  state: { usuarios }                                        // llega al cliente como window.__STATE__
});
// cliente
import { hydrate } from '@duvanjamid/angelite/hydrate';
hydrate(App, document.getElementById('app'));
  • renderToString(Comp, opciones) devuelve solo el HTML del componente; renderPage rellena un shell (<!--app--> y <!--state-->); serializeState(obj) da JSON seguro para incrustar en un <script> (escapa <, >, & y los separadores de línea).
  • setup corre antes de montar, con las globales del DOM puestas: ahí se configura el router para que lea url.
  • ready recibe la instancia y puede devolver una promesa: es donde esperas a tus datos.
Hidratación: lo que hace y lo que no

hydrate() sustituye el HTML del servidor por la app viva en la misma tarea, así que no hay parpadeo. No adopta los nodos existentes: el cliente vuelve a renderizar. Ganas contenido visible antes de que llegue el JavaScript y SEO; no ahorras el trabajo de una hidratación completa. Tampoco hay streaming.

Precompilar plantillas#

Por defecto AngeLite compila las plantillas en el navegador: un solo script, sin build. Si te importa cada KB, conviértelas en JavaScript durante el build y envía solo el runtime (sin parser de expresiones ni compilador): unos 6 KB gzip para una app real, algo menos que Preact (6,2 KB).

<!-- counter.ng.html -->
<button (click)="inc()">Clics: &#123;&#123; count() &#125;&#125;</button>
// counter.js
import { signal, component, mount } from '@duvanjamid/angelite/runtime';
import aot from './counter.ng.html';              // compilada por el plugin

class Counter { count = signal(0); inc() { this.count.update(n => n + 1); } }
component(Counter, { aot });                       // en lugar de template
mount(Counter, document.body);
// build con esbuild
import { aotPlugin } from '@duvanjamid/angelite/tools/aot.mjs';
esbuild.build({ entryPoints: ['counter.js'], bundle: true, minify: true,
  plugins: [aotPlugin({ components: ['ui-button'], members: 'auto', check: 'error' })] });

// o con Vite (recarga al editar una plantilla)
import angelite from '@duvanjamid/angelite/tools/vite.mjs';
export default { plugins: [angelite({ components: ['ui-button'], members: 'auto' })] };

También hay una CLI: node tools/aot.mjs plantilla.html salida.js --components ui-button,ui-card. Una plantilla precompilada produce exactamente el mismo DOM que la compilada en el navegador (lo comprueban los tests).

El compilador también revisa tus plantillas#

  • Elementos desconocidos (<ui-buton>), pipes que no existen y, con members, nombres que el componente no tiene ({{ cuenta() }} cuando solo existe count).
  • members: 'auto' lee los campos y métodos de la clase que está junto a la plantilla (x.ng.html + x.js); también puedes dar la lista.
  • check: 'warn' (por defecto) avisa, 'error' detiene el build, false lo desactiva. No comprueba tipos.
Los módulos opcionales en una app precompilada

Con el runtime solo, importa @duvanjamid/angelite/pipes para los pipes, o usa @duvanjamid/angelite/runtime-plus, que trae portales, @defer y referencias sin el compilador. enableDevWarnings() te avisa si una plantilla necesita uno que falta.

Nunca compiles plantillas con datos de usuario

Con la compilación en el navegador, una plantilla es código. Si una plantilla se construye con texto que no controlas, estás ejecutando ese texto. Precompilar elimina el problema por construcción: el navegador solo recibe el resultado.