Streaming HTML Out-of-Order (OOO) (juga dikenal sebagai Update Parsial Deklaratif) adalah alternatif untuk pemrosesan linear HTML "dari atas ke bawah" yang tradisional. Dengan demikian, developer dapat menentukan placeholder lebih awal dalam dokumen dan "menambalnya" dengan konten yang tiba kemudian dalam aliran jaringan yang sama, atau melalui API streaming JavaScript imperatif baru.
Ringkasan Singkat
Fitur ini terdiri dari dua mekanisme utama:
- Patching Deklaratif (berbasis HTML): Menggunakan petunjuk pemrosesan baru (
<?marker>,<?start>,<?end>) dan<template for>untuk menukar konten di DOM saat di-streaming dari server. - Setelan dan streaming imperatif (berbasis JS): Serangkaian metode baru (misalnya,
streamHTML(),setHTMLUnsafe()) yang memungkinkan setelan HTML atau pengalihan aliran data yang dapat dibaca langsung ke elemen DOM dengan performa native dan eksekusi skrip opsional.
Praktik Terbaik
- Prioritaskan Konten Paruh Atas: Streaming UI penting (header, hero, navigasi utama, konten utama) terlebih dahulu. Gunakan placeholder untuk komponen "mahal" atau berprioritas lebih rendah seperti komentar, iklan, atau konten terkait.
- Terapkan Status Pemuatan Native: Gunakan penanda
<?start>dan<?end>untuk menggabungkan konten pengganti. Browser akan otomatis mengganti semua yang ada di antara penanda ini saat<template for>yang sesuai tiba. - Minimalkan Cumulative Layout Shift (CLS): Selalu berikan dimensi yang stabil untuk placeholder. Jika ukuran konten akhir tidak diketahui, gunakan layar skeleton atau
aspect-ratioCSS untuk menyediakan ruang dan mencegah pergeseran yang mengganggu. - Aksesibilitas & Pengelolaan Fokus: Perhatikan bahwa pembaruan yang tidak berurutan dapat membingungkan pengguna pembaca layar. Gunakan region
aria-liveuntuk update penting dan pastikan fokus tidak hilang jika placeholder yang berisi elemen aktif diganti.
Detail Implementasi
1. Penanda HTML Deklaratif
Browser mencari petunjuk pemrosesan tertentu untuk mengidentifikasi bagian konten yang harus di-patch.
<!-- A simple insertion point -->
<div id="sidebar">
<?marker name="dynamic-comments">
</div>
<!-- A replacement range with loading state -->
<main>
<?start name="main-content">
<div class="skeleton">Loading content...</div>
<?end>
</main>
... (other content) ...
<!-- Later in the same stream, provide the content -->
<template for="dynamic-comments">
<div class="comments">...</div>
</template>
<template for="main-content">
<article>
<h1>The Real Content</h1>
<p>This replaces the skeleton div automatically.</p>
</article>
</template>
Setelah <template> menggantikan titik penyisipan, titik penyisipan tersebut tidak ada lagi, dan tidak dapat digunakan kembali. Namun, <template> dapat berisi titik penyisipan yang dapat digunakan nanti dalam streaming.
<?start name="replaced-content">
<div class="skeleton">
<?end>
<!-- Later in the same stream, provide initial content -->
<template for="replaced-content">
<?start name="replaced-content">
<div>Quickly computed result</div>
<?end>
</template>
<!-- Later in the same stream, provide full content -->
<template for="replaced-content">
<?start name="replaced-content">
<div>Full result</div>
<?end>
</template>
2. Streaming JS Imperatif
Browser menyediakan rangkaian 24 metode (6 tindakan, masing-masing dengan 4 variasi) untuk memperbarui DOM, baik secara statis (dari string) maupun melalui streaming.
Matriks Referensi API
| Tindakan | Statis (Aman) | Streaming (Aman) | Statis (Tidak Aman) | Streaming (Tidak Aman) |
|---|---|---|---|---|
| Set | setHTML() |
streamHTML() |
setHTMLUnsafe() |
streamHTMLUnsafe() |
| Ganti | replaceWithHTML() |
streamReplaceWithHTML() |
replaceWithHTMLUnsafe() |
streamReplaceWithHTMLUnsafe() |
| Sebelum | beforeHTML() |
streamBeforeHTML() |
beforeHTMLUnsafe() |
streamBeforeHTMLUnsafe() |
| Tambahkan di awal | prependHTML() |
streamPrependHTML() |
prependHTMLUnsafe() |
streamPrependHTMLUnsafe() |
| Penambahan | appendHTML() |
streamAppendHTML() |
appendHTMLUnsafe() |
streamAppendHTMLUnsafe() |
| Setelah | afterHTML() |
streamAfterHTML() |
afterHTMLUnsafe() |
streamAfterHTMLUnsafe() |
Konsep Utama
- Aman vs. Tidak Aman:
- Metode aman (misalnya,
setHTML()) menggunakan pembersih bawaan secara default untuk menghapus konten yang berpotensi berbahaya seperti tag<script>. Anda dapat meneruskan objekSanitizerkustom dalam opsi untuk diterapkan selain pembersih default. - Metode Unsafe (misalnya,
setHTMLUnsafe()) tidak melakukan sanitasi secara default, tetapi Anda masih dapat meneruskanSanitizerkustom. Untuk mengizinkan skrip dijalankan di konten baru, Anda harus meneruskan{ runScripts: true }secara eksplisit dalam opsi. PENTING: Hanya gunakan metode yang tidak aman dengan konten tepercaya jika metode yang aman tidak akan berfungsi.
- Metode aman (misalnya,
- Statis vs. Streaming:
- Metode statis mengambil string (atau
TrustedHTML) dan langsung menerapkannya. - Metode Streaming menampilkan
WritableStream. Hal ini memungkinkan Anda menyalurkan konten (misalnya, dari responsfetch) langsung ke DOM, dan browser akan merendernya secara bertahap saat potongan data tiba.
- Metode statis mengambil string (atau
Contoh: Melakukan Streaming Respons Pengambilan
Anda dapat menyalurkan respons pengambilan langsung ke DOM tanpa penanganan potongan manual.
const main = document.querySelector('main');
const response = await fetch('/api/partial-update');
// Pipe the stream directly into the element
await response.body
.pipeThrough(new TextDecoderStream())
.pipeTo(main.streamHTMLUnsafe({ runScripts: true }));
// Or use the `textStream()` convenience method which streams directly without needing the intermediate `TextDecoderStream()` step
await response
.textStream()
.pipeTo(main.streamHTMLUnsafe({ runScripts: true }))
Matriks Referensi Kasus Penggunaan
- Island Architecture: Menghidrasi komponen interaktif independen di atas HTML statis tanpa runtime framework penuh.
- Blok "Berat" yang Ditangguhkan: Menayangkan shell halaman dengan segera dan menstreaming komponen yang berat database saat tersedia.
- Update Aplikasi Web Satu Halaman (SPA): Streaming transisi UI besar di SPA menggunakan
streamHTML(), bukan menunggu payload JSON lengkap dan konstruksi DOM manual.
Anti-Pola & Peringatan (JANGAN LAKUKAN INI)
- Jangan gunakan streaming OOO untuk semuanya. Fragmentasi halaman yang berlebihan dapat meningkatkan kompleksitas streaming dan berpotensi menunda konten jika browser menghabiskan terlalu banyak waktu untuk melakukan patching pada node kecil.
- Jangan abaikan Jenis Tepercaya. Saat menggunakan metode
*Unsafedi lingkungan dengan Jenis Tepercaya yang diterapkan, pastikan Anda meneruskan objek kebijakan yang telah dibersihkan jika diperlukan. - Jangan berasumsi tentang urutan eksekusi skrip. Skrip di dalam
<template for>akan dieksekusi segera setelah di-patch ke dalam dokumen. Jangan mengandalkan eksekusi template yang ditangguhkan dalam urutan tertentu dibandingkan dengan template yang ditangguhkan lainnya. - Jangan gunakan tanpa strategi polyfill. Pastikan Anda memiliki penggantian atau menggunakan polyfill (
html-setters-polyfilldantemplate-for-polyfill) untuk mendukung browser yang diperlukan hingga fitur ini menjadi Baseline.
Strategi penggantian
Anda dapat menggunakan polyfill seperti template-for-polyfill dan html-setters-polyfill untuk mengemulasi platform API bagi browser yang tidak mendukung.
Browser tanpa dukungan html-processing-instructions akan mengurai <?marker>, <?start>, dan <?end> sebagai komentar. template-for-polyfill masih mengenali komentar ini sebagai penanda, jadi pertahankan sintaksis petunjuk pemrosesan yang sama untuk semua browser. Jangan menulis ulang penanda sebagai komentar atau elemen HTML untuk browser yang tidak mendukung.