Wstępne renderowanie oznacza wysyłanie z serwera znaczników elementu niestandardowego, w tym jego drzewa shadow, dzięki czemu komponent jest stylizowany i strukturyzowany przed uruchomieniem kodu JavaScript. Głównym elementem podstawowym platformy jest deklaratywny model Shadow DOM (DSD): parser dołącza element shadow root z samego kodu HTML, a element jest później uaktualniany („hydratowany”) przez klasę, jeśli jest zarejestrowana. Używaj tego wzorca do renderowania po stronie serwera, strumieniowania i unikania wyświetlania nieostylowanej zawartości (FOUC).
Tworzenie deklaratywnego korzenia shadow
Zagnieźdź element <template> z atrybutem shadowrootmode jako pierwszy element podrzędny hosta. Parser dołącza zawartość jako korzeń shadow i usuwa szablon – skrypt nie jest potrzebny.
<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>
Czas nawodnienia
Gdy pasująca klasa zostanie zarejestrowana, przeglądarka uaktualni hosta. Ponieważ element shadow root już istnieje, konstruktor nie może utworzyć drugiego:
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'));
}
}
W prawie wszystkich przypadkach ustaw shadowrootmode="open" (nie closed), aby skrypty i narzędzia mogły dotrzeć do this.shadowRoot w celu hydratacji. Aby zserializować istniejący element shadow root z powrotem do kodu HTML na serwerze lub w testach, użyj getHTML({ serializableShadowRoots: true }) i dołącz element główny za pomocą serializable: true.
Unikanie FOUC
DSD renderuje się prawidłowo przy pierwszym renderowaniu tylko wtedy, gdy style są dostarczane w szablonie deklaratywnym. Aby zachować te ustawienia:
- Umieść kod CSS komponentu wewnątrz elementu
<template shadowrootmode>(tag<style>lub<link>), a nie w osobnym arkuszu wczytywanym przez skrypt. - Używaj
my-card:not(:defined) { … }tylko w przypadku stylów, które muszą czekać na JS – zwykle są to reguły na poziomie strony, takie jak zanikanie w instancjach renderowanych po stronie klienta, które nie zostały wyrenderowane po stronie serwera. Znajdują się one w arkuszu stylów light-DOM (strony), ponieważ nie ma tam jeszcze shadow root, a selektory:hostnie mają zastosowania z zewnątrz. Style strukturalne w szablonie DSD nie wymagają takiego zabezpieczenia. - Nie renderuj ponownie drzewa shadow podczas hydratacji. Traktuj znaczniki serwera jako autorytatywne i dołącz do nich działanie.
Strategie zastępcze
W silnikach, które nie analizują elementu shadowrootmode, szablon jest nieaktywny, a jego zawartość nie jest dołączana jako korzeń shadow. Wykryj ten element (Object.hasOwn(HTMLTemplateElement.prototype, 'shadowRootMode')) i jeśli go nie ma, podczas uaktualniania dołącz shadow root z tego samego znacznika, aby komponent nadal się renderował.