La renderización previa implica enviar el lenguaje de marcado de un elemento personalizado, incluido su árbol de shadow DOM, desde el servidor, de modo que el componente tenga estilo y estructura antes de que se ejecute cualquier código JavaScript. La primitiva principal de la plataforma para esto es el Shadow DOM declarativo (DSD): el analizador adjunta la raíz de sombra solo desde el HTML, y el elemento se actualiza ("hidrata") más adelante por su clase, si hay una registrada. Usa este patrón para la renderización del servidor, la transmisión y evitar un destello de contenido sin diseño (FOUC).
Crea una raíz de shadow DOM declarativo
Anida un elemento <template> con el atributo shadowrootmode como el primer elemento secundario del host. El analizador adjunta su contenido como una raíz de sombra y quita la plantilla; no se necesita ninguna secuencia de comandos.
<my-card>
<template shadowrootmode="open">
<style>
.card { border: 1px solid; border-radius: 8px; padding: 1rem; }
</style>
<div class="card">
<slot name="header"></slot>
<div class="body"><slot></slot></div>
</div>
</template>
<!-- Light DOM children are projected into the slots above. -->
<h2 slot="header">Server-rendered title</h2>
<p>Visible and styled before JS loads.</p>
</my-card>
Momento de la hidratación
Cuando se registra la clase coincidente, el navegador actualiza el host. Debido a que la raíz de sombra ya existe, el constructor no debe crear una segunda de forma ciega:
class MyCard extends HTMLElement {
constructor() {
super();
// Reuse the server-rendered shadow root if present; only create one if absent.
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' });
this.shadowRoot.append(template.content.cloneNode(true));
}
// Hydrate in place: attach listeners once against existing markup (not unguarded
// in connectedCallback, which re-runs if the element moves in the DOM), and do
// NOT re-render; that would discard the server's DOM and flash.
this.shadowRoot.getElementById('toggle')
?.addEventListener('click', () => this.toggleAttribute('open'));
}
}
Establece shadowrootmode="open" (no closed) para casi todos los casos, de modo que las secuencias de comandos y las herramientas puedan llegar a this.shadowRoot para hidratarse. Para volver a serializar una raíz de shadow DOM existente en HTML en el servidor o en las pruebas, usa getHTML({ serializableShadowRoots: true }) y adjunta la raíz con serializable: true.
Cómo evitar el FOUC
El DSD se renderiza correctamente en el primer procesamiento de imagen solo si los estilos se envían dentro de la plantilla declarativa. Para preservar eso, haz lo siguiente:
- Coloca el CSS del componente dentro de
<template shadowrootmode>(una etiqueta<style>o un<link>), no en una hoja separada cargada por una secuencia de comandos. - Vincula con
my-card:not(:defined) { … }solo para los estilos que deben esperar a JS, por lo general, reglas de protección a nivel de la página, como un desvanecimiento en las instancias renderizadas por el cliente que no pasan la SSR. Estos se encuentran en la hoja de estilo del DOM de luz (página), ya que la raíz de sombra aún no existe allí y los selectores:hostno se aplican desde el exterior. Los estilos estructurales dentro de la plantilla de DSD no necesitan esa protección. - No vuelvas a renderizar el árbol de sombra en la hidratación. Tratar el lenguaje de marcado del servidor como autoritativo y adjuntarle un comportamiento
Estrategias de resguardo
En los motores que no analizan shadowrootmode, la plantilla está inerte y su contenido no se adjunta como una raíz de sombra. Detecta esto (Object.hasOwn(HTMLTemplateElement.prototype, 'shadowRootMode')) y, si no está presente, adjunta la raíz de sombra de forma imperativa desde el mismo lenguaje de marcado durante la actualización para que el componente se siga renderizando.