Ir al contenido

Hidratación

La hidratación es el momento en que el cliente adopta el marcado que el servidor ya pintó, en vez de tirarlo y volver a renderizar. En wu es un slot del ciclo de vida, no una API aparte.

wu.mount('cart', '#wu-app-cart') es la misma llamada tanto si la página se renderizó en servidor como si no. Por dentro, el sandbox ejecuta _detectSsrRoot(hostContainer):

  1. Si el contenedor no tiene el atributo data-wu-ssr → montaje normal, no cambia nada.
  2. Si el navegador ya materializó el Declarative Shadow DOM (existe hostContainer.shadowRoot) → busca [data-wu-root] dentro y lo reutiliza.
  3. Si el <template shadowrootmode> sigue en el light DOM (navegador sin DSD nativo y sin polyfill en la página) → attachShadow(), mueve el contenido del template dentro, elimina el template y continúa como si hubiera sido nativo.

Cuando encuentra una raíz del servidor, wu no la limpia. Marca el elemento [data-wu-root] existente como contenedor de la app, inserta sus estilos base antes del marcado del servidor (para no alterar el orden de pintado) y activa isHydration en el sandbox.

Luego wu-core elige el slot:

const hydrating = sandbox.isHydration && typeof lifecycle.hydrate === 'function';
if (hydrating) {
await lifecycle.hydrate(sandbox.container, { appName, props: this._ssrProps(appName) });
} else {
await lifecycle.mount(sandbox.container);
}

_ssrProps(appName) lee window.__WU_SSR_STATE__[appName] y recurre a {} si el script falta o no se puede leer. El registro de la app montada lleva hydrated: true para que los diagnósticos distingan ambos caminos.

hydrate(container: HTMLElement, ctx: { appName: string; props: Record<string, any> }): void | Promise<void>

Escrito a mano:

wu.define('cart', {
mount(container) {
container.innerHTML = render(state);
wireEvents(container);
},
// Se llama solo cuando el contenedor vino de wu-framework/server
hydrate(container, { props }) {
// El DOM ya es correcto: basta con conectar el comportamiento
wireEvents(container);
},
unmount(container) { container.innerHTML = ''; },
});

Los adaptadores lo conectan por ti donde el framework lo soporta:

Adaptador hydrate Cómo
React hydrateRoot: se engancha al DOM existente
Vue createSSRApp: hidrata en vez de montar
Preact, Svelte, Solid y el resto no recurre a mount sobre el marcado del servidor

Tres formas, un mismo destino: window.__WU_SSR_STATE__.

// 1. Un shell sencillo
res.end(template.replace('<!--state-->', renderStateScript(state)));
// 2. Next: WuMount las publica en un efecto antes de llamar a wu.mount()
<WuAppSSR name="cart" module={cartServer} props={{ userId: 42 }} />
<!-- 3. Astro: se emite un <script type="application/json" data-wu-ssr-state="cart">
por app, y lo lee el script de hidratación empaquetado -->
<WuAppSSR name="cart" module={cartServer} props={{ userId: 42 }} />

Ambas integraciones se fusionan con el objeto existente en vez de reemplazarlo, así que varias instancias de WuAppSSR conviven en una misma página.

Orden respecto a la hidratación del propio shell

Sección titulada «Orden respecto a la hidratación del propio shell»

En Next, WuMount se ejecuta en un useEffect, es decir después de que React haya hidratado la página. Publica las props, importa wu-framework dinámicamente y llama a wu.mount(name, '#' + containerId). Si el efecto se limpia mientras el montaje sigue en vuelo, desmonta la app, de modo que una navegación rápida no puede dejar una app enganchada a un contenedor desprendido.

En Astro, el <script> empaquetado consulta cada div[data-wu-app][data-wu-hydrate="true"] y los monta al cargar.

Situación Resultado
shadowMode: 'closed' El cliente no puede leer hostContainer.shadowRoot, así que no se detecta ninguna raíz: la app se monta desde cero. Usa closed solo para marcado inerte.
declarative: false Marcado plano, sin shadow root, data-wu-ssr="flat". La detección no encuentra ningún [data-wu-root] dentro de un shadow root → montaje normal.
El render de la app lanzó un error renderApps emitió un contenedor vacío sin data-wu-ssr → montaje normal. Esa es la degradación prevista.
Falta el slot hydrate Monta encima del marcado y escribe una línea de depuración.
Props que no coinciden Nadie lo detecta. Lo que haya en window.__WU_SSR_STATE__[appName] es lo que recibe hydrate; si el servidor renderizó con otros datos, React o Vue avisarán o parchearán, exactamente como en cualquier montaje SSR.
  • Haz curl a la página. Si no ves data-wu-ssr="open" y un <template shadowrootmode>, el problema está en el servidor, no en la hidratación.
  • En DevTools, el contenedor debería mostrar #shadow-root (open) con <div data-wu-root data-wu-hydrated="true"> dentro.
  • Activa el registro de depuración de wu: los caminos de hidratación emiten SSR markup detected for <app> — hydrating in place, o bien <app> has server markup but no hydrate() slot — mounting over it.