Pra-rendering elemen kustom

Pra-rendering berarti mengirimkan markup elemen kustom — termasuk shadow tree-nya — dari server, sehingga komponen diberi gaya dan disusun sebelum JavaScript dijalankan. Primitif platform utama untuk hal ini adalah Shadow DOM Deklaratif (DSD): parser melampirkan shadow root dari HTML saja, dan elemen tersebut nantinya diupgrade ("dihidrasi") oleh class-nya jika ada yang terdaftar. Gunakan pola ini untuk rendering sisi server, streaming, dan menghindari flash konten tanpa gaya (FOUC).

Membuat root shadow deklaratif

Tempatkan <template> dengan atribut shadowrootmode sebagai turunan pertama host. Parser melampirkan kontennya sebagai shadow root dan menghapus template; tidak diperlukan skrip.

<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>

Waktu hidrasi

Saat class yang cocok didaftarkan, browser akan mengupgrade host. Karena root bayangan sudah ada, konstruktor tidak boleh membuat root bayangan kedua secara sembarangan:

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'));
  }
}

Tetapkan shadowrootmode="open" (bukan closed) untuk hampir semua kasus, sehingga skrip dan alat dapat menjangkau this.shadowRoot untuk di-hydrate. Untuk melakukan serialisasi root bayangan yang ada kembali ke HTML di server atau dalam pengujian, gunakan getHTML({ serializableShadowRoots: true }) dan lampirkan root dengan serializable: true.

Menghindari FOUC

DSD dirender dengan benar pada first paint hanya jika gaya dikirimkan di dalam template deklaratif. Untuk mempertahankan hal tersebut:

  • Tempatkan CSS komponen di dalam <template shadowrootmode> (tag <style> atau <link>), bukan di sheet terpisah yang dimuat skrip.
  • Gabungkan dengan my-card:not(:defined) { … } hanya untuk gaya yang harus menunggu JS — biasanya aturan pengaman tingkat halaman seperti fade-out pada instance yang dirender klien yang melewatkan penerusan SSR. Elemen ini berada di stylesheet light-DOM (halaman), karena root shadow belum ada di sana dan pemilih :host tidak berlaku dari luar. Gaya struktural di dalam template DSD tidak memerlukan perlindungan tersebut.
  • Jangan merender ulang hierarki shadow pada hidrasi. Memperlakukan markup server sebagai otoritatif dan melampirkan perilaku ke markup tersebut.

Strategi penggantian

Di mesin yang tidak mengurai shadowrootmode, template bersifat inert dan kontennya tidak dilampirkan sebagai shadow root. Deteksi ini (Object.hasOwn(HTMLTemplateElement.prototype, 'shadowRootMode')) dan, jika tidak ada, lampirkan shadow root secara imperatif dari markup yang sama selama upgrade agar komponen tetap dirender.