Il pre-rendering consiste nell'invio del markup di un elemento personalizzato, incluso il relativo shadow tree, dal server, in modo che lo stile e la struttura del componente vengano definiti prima dell'esecuzione di qualsiasi codice JavaScript. La primitiva principale della piattaforma per questo è Declarative Shadow DOM (DSD): l'analizzatore collega la shadow root solo dall'HTML e l'elemento viene successivamente aggiornato ("idratato") dalla sua classe, se ne è registrata una. Utilizza questo pattern per il rendering lato server, lo streaming e per evitare un flash di contenuti non formattati (FOUC).
Creazione di una shadow root dichiarativa
Nidifica un elemento <template> con l'attributo shadowrootmode come primo elemento secondario dell'host. Il parser associa i suoi contenuti come shadow root e rimuove il template; non è necessario alcuno script.
<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>
Tempistica dell'idratazione
Quando la classe corrispondente viene registrata, il browser esegue l'upgrade dell'host. Poiché la shadow root esiste già, il costruttore non deve crearne una seconda alla cieca:
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'));
}
}
Imposta shadowrootmode="open" (non closed) per quasi tutti i casi, in modo che gli script e gli strumenti possano raggiungere this.shadowRoot per l'idratazione. Per serializzare una shadow root esistente in HTML sul server o nei test, utilizza getHTML({ serializableShadowRoots: true }) e collega la root con serializable: true.
Evitare il FOUC
DSD esegue il rendering corretto al primo paint solo se gli stili vengono forniti all'interno del template dichiarativo. Per preservare questo aspetto:
- Inserisci il CSS del componente all'interno di
<template shadowrootmode>(un tag<style>o<link>), non in un foglio separato caricato tramite script. - Abbina
my-card:not(:defined) { … }solo per gli stili che devono attendere il codice JS, in genere regole di protezione a livello di pagina, come una dissolvenza in uscita su istanze sottoposte a rendering lato client che non superano il passaggio SSR. Questi si trovano nel foglio di stile del DOM chiaro (pagina), poiché la shadow root non esiste ancora e i selettori:hostnon si applicano dall'esterno. Gli stili strutturali all'interno del template DSD non richiedono questa protezione. - Non eseguire nuovamente il rendering dello shadow tree durante l'idratazione. Tratta il markup del server come autoritativo e associa il comportamento.
Strategie di riserva
Nei motori che non analizzano shadowrootmode, il modello è inerte e i suoi contenuti non vengono collegati come shadow root. Rileva questo (Object.hasOwn(HTMLTemplateElement.prototype, 'shadowRootMode')) e, se assente, collega lo shadow root in modo imperativo dallo stesso markup durante l'upgrade in modo che il componente venga comunque visualizzato.