Declarative Shadow DOM
Declarative Shadow DOM คืออะไร
หัวข้อที่มีชื่อว่า “Declarative Shadow DOM คืออะไร”Declarative Shadow DOM (DSD) ช่วยให้คุณแนบ shadow root เข้ากับ element โดยใช้แค่ HTML โดยไม่ต้องใช้ JavaScript เลย สิ่งนี้เหมาะอย่างยิ่งสำหรับ server-side rendering (SSR) ที่ shadow tree สามารถถูกรวมไว้ใน HTML response เริ่มต้น และ browser จะแนบ shadow root ให้ก่อนที่ JavaScript ใด ๆ จะทำงาน
attribute shadowrootmode
หัวข้อที่มีชื่อว่า “attribute shadowrootmode”การวาง <template shadowrootmode="open"> (หรือ "closed") เป็น child โดยตรงของ element จะทำให้ browser แนบ shadow root เข้ากับ element แม่โดยอัตโนมัติ และย้ายเนื้อหาของ template เข้าไปในนั้น
<my-card> <template shadowrootmode="open"> <style> p { color: #0070f3; font-weight: bold; } </style> <p>This content lives in the shadow root.</p> <slot></slot> </template> Light DOM content here (goes into the slot)</my-card>แท็ก <template> จะถูก browser ใช้งานไปและถูกแทนที่ด้วย shadow root ที่ถูกแนบ ไม่ต้องใช้ JavaScript เลย element จะมี shadow root ทันทีตอน parse
เรื่องราวของการ hydration
หัวข้อที่มีชื่อว่า “เรื่องราวของการ hydration”เมื่อ JavaScript โหลดขึ้นมา customElements.define('my-card', MyCard) จะอัปเกรด element หาก class เรียก attachShadow ใน constructor ของตัวเอง browser จะข้ามการสร้าง shadow root ตัวที่สอง (เพราะมีอยู่แล้ว) และนำตัวที่มีอยู่กลับมาใช้ใหม่ นั่นหมายความว่าเนื้อหาที่ถูก render จากเซิร์ฟเวอร์จะถูกรักษาไว้ — คุณสมบัตินี้สำคัญมากสำหรับ SSR hydration
class MyCard extends HTMLElement { connectedCallback() { // Shadow root already attached declaratively — shadowRoot is available const shadow = this.shadowRoot; // not null if (shadow) { // hydrate: add event listeners, update state, etc. } }}customElements.define('my-card', MyCard);หมายเหตุเรื่องการรองรับของ browser
หัวข้อที่มีชื่อว่า “หมายเหตุเรื่องการรองรับของ browser”Declarative Shadow DOM รองรับใน Chrome 90+, Edge 90+, Safari 16.4+ และ Firefox 123+ ใน browser รุ่นเก่า attribute <template shadowrootmode> จะไม่เป็นที่รู้จัก และ template จะยังคงเฉื่อย (inert) อยู่ ให้ใส่ polyfill สำหรับ browser รุ่นเก่า:
// Polyfill for older browsersdocument.querySelectorAll('template[shadowrootmode]').forEach(function(t) { t.parentElement.attachShadow({ mode: t.getAttribute('shadowrootmode') }) .appendChild(t.content.cloneNode(true)); t.remove();});เดโมที่รันได้
หัวข้อที่มีชื่อว่า “เดโมที่รันได้”LivePreview ด้านล่างแสดง <dsd-card> ที่มี <template shadowrootmode="open"> อยู่ใน HTML แล้ว slot จะรับข้อความจาก light DOM ไม่ต้องใช้ JavaScript เพื่อให้ shadow แนบ
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
Declarative Shadow DOM (<template shadowrootmode>) | shadow root มีอยู่ตั้งแต่ parse HTML เลย ไม่ต้องรอ JavaScript โหลดเพื่อ render — สำคัญมากสำหรับ SSR และ first paint | server ต้อง duplicate markup ของ template ไว้ในทุก instance ของ component ทำให้ payload HTML ใหญ่ขึ้นตามจำนวน component บนหน้า |
| attachShadow แบบ JavaScript ปกติ | markup ฝั่ง server เล็กกว่า ไม่มี template ซ้ำ | ต้องรอ JavaScript โหลดและรันก่อน ระหว่างนั้นผู้ใช้อาจเห็นเนื้อหาไม่มีสไตล์ (FOUC) หรือไม่เห็นเนื้อหาเลย |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- สับสนว่าต้องเรียก
attachShadowซ้ำในconnectedCallback— ถ้า shadow root ถูกแนบแบบ declarative ไว้แล้ว browser จะข้ามให้เฉย ๆ ไม่ throw error แต่ถ้า logic ของคุณ assume ว่าต้องสร้างเองเสมอ อาจเขียน initialization ผิดที่และเผลอทับ shadow root ที่มีอยู่แล้ว - ไม่เช็คการรองรับของ browser ก่อน deploy — browser รุ่นเก่าที่ไม่รองรับ DSD จะ render
<template>เป็น element ธรรมดาที่มองเห็นได้ ทำให้ markup ภายในหลุดออกมาแสดงตรง ๆ กลางหน้า - ไม่ตรวจสอบ
this.shadowRootก่อน hydrate ฝั่ง client — ถ้า client-side code สร้าง shadow root ใหม่ทับของเดิมโดยไม่เช็คก่อน จะทำให้เนื้อหาที่ render จาก server หายไปและต้อง render ซ้ำโดยไม่จำเป็น เสียประโยชน์ของ DSD ไปเปล่า ๆ
💡 ตัวอย่างจากของจริง
Lit SSR (
@lit-labs/ssr) — render component เป็น Declarative Shadow DOM บนฝั่ง server แล้วส่ง HTML ที่มี shadow tree ครบไปให้ browser ทันที ลด time-to-first-paint และไม่ต้อง render ซ้ำตอน hydrateAdobe Spectrum Web Components — ออกแบบให้ compatible กับ SSR และ Declarative Shadow DOM เพื่อให้ design system ขนาดใหญ่ render ได้เร็วตั้งแต่ first paint โดยไม่ต้องรอ JavaScript hydrate