Wstępne renderowanie elementów niestandardowych

Funkcje internetowe: Deklaratywny shadow DOM

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 :host nie 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ł.