การแสดงผลล่วงหน้าหมายถึงการส่งมาร์กอัปขององค์ประกอบที่กำหนดเอง ซึ่งรวมถึง Shadow Tree จากเซิร์ฟเวอร์ เพื่อให้คอมโพเนนต์ได้รับการจัดรูปแบบและโครงสร้างก่อนที่ JavaScript จะทำงาน องค์ประกอบพื้นฐานหลักของแพลตฟอร์มสำหรับกรณีนี้คือ Declarative Shadow DOM (DSD): โปรแกรมแยกวิเคราะห์จะแนบรูท Shadow จาก HTML เพียงอย่างเดียว และองค์ประกอบจะได้รับการอัปเกรด ("ไฮเดรต") ในภายหลังตามคลาสหากมีการลงทะเบียนไว้ ใช้รูปแบบนี้สำหรับการแสดงผลฝั่งเซิร์ฟเวอร์ การสตรีม และการหลีกเลี่ยง Flash of Unstyled Content (FOUC)
การเขียน Shadow Root แบบประกาศ
ฝัง <template> ที่มีแอตทริบิวต์ shadowrootmode เป็นองค์ประกอบย่อยแรกของโฮสต์ โปรแกรมแยกวิเคราะห์จะแนบเนื้อหาเป็น Shadow Root และนำเทมเพลตออก โดยไม่จำเป็นต้องใช้สคริปต์
<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>
เวลาที่ดื่มน้ำ
เมื่อลงทะเบียนคลาสที่ตรงกันแล้ว เบราว์เซอร์จะอัปเกรดโฮสต์ เนื่องจากมี Shadow Root อยู่แล้ว ตัวสร้างจึงต้องไม่สร้าง Shadow Root รายการที่ 2 โดยไม่ตรวจสอบก่อน ดังนี้
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'));
}
}
ตั้งค่า shadowrootmode="open" (ไม่ใช่ closed) สำหรับเกือบทุกกรณี เพื่อให้สคริปต์และเครื่องมือเข้าถึง this.shadowRoot เพื่อเติมข้อมูลได้ หากต้องการแปลง Shadow Root ที่มีอยู่กลับเป็น HTML บนเซิร์ฟเวอร์หรือในการทดสอบ ให้ใช้ getHTML({ serializableShadowRoots: true }) และแนบรูทด้วย serializable: true
การหลีกเลี่ยง FOUC
DSD จะแสดงผลอย่างถูกต้องในการแสดงผลครั้งแรกก็ต่อเมื่อสไตล์แสดงอยู่ภายในเทมเพลตที่ประกาศ วิธีรักษาข้อมูลดังกล่าวมีดังนี้
- ใส่ CSS ของคอมโพเนนต์ไว้ใน
<template shadowrootmode>(แท็ก<style>หรือ<link>) ไม่ใช่ในชีตที่โหลดสคริปต์แยกต่างหาก - จับคู่กับ
my-card:not(:defined) { … }เฉพาะสไตล์ที่ต้องรอ JS ซึ่งโดยปกติแล้วจะเป็นกฎป้องกันระดับหน้าเว็บ เช่น การเฟดออกในอินสแตนซ์ที่แสดงผลฝั่งไคลเอ็นต์ซึ่งพลาดการส่ง SSR โดยจะอยู่ในสไตล์ชีต Light-DOM (หน้าเว็บ) เนื่องจากยังไม่มี Shadow Root และตัวเลือก:hostจะไม่มีผลจากภายนอก รูปแบบโครงสร้างภายในเทมเพลต DSD ไม่จำเป็นต้องมีการป้องกันดังกล่าว - อย่าแสดงผล Shadow Tree ซ้ำเมื่อเกิดการไฮเดรชัน ถือว่ามาร์กอัปของเซิร์ฟเวอร์เป็นแหล่งข้อมูลที่เชื่อถือได้และแนบลักษณะการทำงานเข้ากับมาร์กอัปนั้น
กลยุทธ์สำรอง
ในเครื่องมือที่ไม่ได้แยกวิเคราะห์ shadowrootmode เทมเพลตจะไม่มีการทำงานและเนื้อหาจะไม่แนบเป็น Shadow Root ตรวจหาองค์ประกอบนี้ (Object.hasOwn(HTMLTemplateElement.prototype, 'shadowRootMode')) และหากไม่มี ให้แนบ Shadow Root จากมาร์กอัปเดียวกันในระหว่างการอัปเกรดเพื่อให้คอมโพเนนต์ยังคงแสดงผล