Web Components คืออะไร?
ปัญหา: นำกลับมาใช้ซ้ำได้โดยไม่ต้องพึ่งเฟรมเวิร์ก
หัวข้อที่มีชื่อว่า “ปัญหา: นำกลับมาใช้ซ้ำได้โดยไม่ต้องพึ่งเฟรมเวิร์ก”ก่อนจะมี Web Components การแชร์ UI widget ระหว่างโปรเจกต์หมายถึงการคัดลอกและวางมาร์กอัปไปมา หรือไม่ก็ต้องผูกตัวเองกับเฟรมเวิร์กใดเฟรมเวิร์กหนึ่ง component ปุ่มของ React ไม่สามารถนำไปวางในแอป Vue ได้ การ์ดของ Svelte ก็นำกลับมาใช้ซ้ำในโปรเจกต์ Angular ไม่ได้
Web Components แก้ปัญหานี้ด้วยมาตรฐานสามอย่างที่มากับ browser:
- Custom Elements — นิยาม HTML tag ใหม่ในรูปแบบ ES class
- Shadow DOM — ห่อหุ้ม DOM และ CSS ของ element ไม่ให้สไตล์รั่วเข้าหรือออก
- HTML Templates และ Slots — ประกาศชิ้นส่วนมาร์กอัปที่นำกลับมาใช้ซ้ำได้และจุดแทรกเนื้อหาแบบ declarative
ผลลัพธ์คือ component ที่มีคุณสมบัติ:
| คุณสมบัติ | ความหมาย |
|---|---|
| นำกลับมาใช้ซ้ำได้ | ใช้งานได้ในบริบท HTML ใด ๆ กับเฟรมเวิร์กใดก็ได้หรือไม่ใช้เลยก็ได้ |
| ห่อหุ้มเป็นเอกเทศ | มาร์กอัปและสไตล์ภายในถูกแยกออกจากหน้าเพจ |
| เป็นของเนทีฟ | ไม่ต้อง transpile ไม่ต้องมี virtual DOM ไม่ต้องมีไลบรารีตอนรันไทม์ |
Component ของเฟรมเวิร์ก เทียบกับ Web Components
หัวข้อที่มีชื่อว่า “Component ของเฟรมเวิร์ก เทียบกับ Web Components”Component ของเฟรมเวิร์ก (React, Vue, Svelte) อยู่ภายในรันไทม์ของเฟรมเวิร์กตัวนั้นทั้งหมด browser ไม่เคยเห็น <MyButton> เลย — เห็นแค่สิ่งที่เฟรมเวิร์กเรนเดอร์ออกมาแทน
<!-- React JSX — compiled away, never in the DOM --><MyButton variant="primary">Save</MyButton>
<!-- Web Component — a real DOM node the browser owns --><my-button variant="primary">Save</my-button>Web Components เป็น DOM node จริง ๆ คุณ query ได้ด้วย document.querySelector ดักฟัง event ของตัวเองด้วย addEventListener และตรวจสอบใน DevTools ได้ — เหมือนกับ element ที่มีมาในตัวทุกประการ
การรองรับของ browser ในปัจจุบัน
หัวข้อที่มีชื่อว่า “การรองรับของ browser ในปัจจุบัน”เอนจินเรนเดอร์หลักทั้งสี่ตัวรองรับสเปก Web Components v1 อย่างครบถ้วน:
- Chrome / Edge — รองรับเต็มรูปแบบตั้งแต่ปี 2018
- Firefox — รองรับเต็มรูปแบบตั้งแต่ปี 2018
- Safari — รองรับ Custom Elements เต็มรูปแบบตั้งแต่ Safari 10.1 (2017); ส่วน Shadow DOM v1 ตั้งแต่ Safari 10 (2016)
ข้อยกเว้นเดียวคือ customized built-ins (การ extend element ที่มีอยู่แล้ว เช่น HTMLButtonElement) Safari ไม่รองรับและได้ระบุไว้ว่าจะไม่รองรับ — autonomous custom elements (extend HTMLElement) จึงเป็นตัวเลือกที่พกพาได้ เรื่องนี้จะกล่าวถึงในบทเรียนสุดท้ายของโมดูลนี้
Custom element ก็เป็นแค่ class ตัวหนึ่ง
หัวข้อที่มีชื่อว่า “Custom element ก็เป็นแค่ class ตัวหนึ่ง”โดยแก่นแล้ว custom element คือ ES class ที่ extend HTMLElement และถูกลงทะเบียนด้วยชื่อ tag:
class MyGreeting extends HTMLElement { connectedCallback() { this.textContent = 'Hello from a custom element!'; }}
customElements.define('my-greeting', MyGreeting);หลังจาก define แล้ว tag <my-greeting> ใด ๆ ในเอกสาร — ไม่ว่าจะมีอยู่แล้วหรือเพิ่มเข้ามาในอนาคต — จะถูกอัปเกรดเป็นอินสแตนซ์ของ MyGreeting โดยอัตโนมัติ
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| Web Components (มาตรฐานเนทีฟ) | ไม่ผูกกับเฟรมเวิร์กใด ใช้งานได้ทุกที่ที่มี browser ไม่ต้อง transpile หรือมี runtime library | DX สู้เฟรมเวิร์กอย่าง React/Vue ไม่ได้ — ไม่มี reactive state, JSX, หรือ ecosystem tooling ให้ในตัว |
| Customized built-ins (ส่วนหนึ่งของสเปก) | สืบทอด semantics ของ element เนทีฟได้ฟรี | Safari ปฏิเสธที่จะรองรับ ทำให้ใช้งานจริงข้าม browser ไม่ได้เต็มที่ |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- คิดว่า Web Components มาแทนที่เฟรมเวิร์กได้ทั้งหมด — จริง ๆ แล้ว Web Components เป็น building block ระดับ browser ไม่ได้มี routing, state management หรือ tooling ระดับแอปให้เหมือนเฟรมเวิร์ก
- ลืมกฎเครื่องหมายขีดตอนตั้งชื่อ tag — ชื่อ tag ที่ไม่มีขีดจะไม่ถูกยอมรับเป็น custom element ที่ถูกต้องตามสเปก
- สมมติว่าทั้งสามมาตรฐาน (Custom Elements, Shadow DOM, Templates/Slots) รองรับเท่ากันทุก browser — customized built-ins เป็นข้อยกเว้นสำคัญที่ Safari ไม่รองรับ ทั้งที่ส่วนอื่นรองรับครบแล้ว
💡 ตัวอย่างจากของจริง
YouTube — สร้างฟรอนต์เอนด์ทั้งเว็บด้วย custom elements ของตัวเอง แสดงให้เห็นว่ามาตรฐานเนทีฟสเกลไปถึงระดับ production ขนาดใหญ่ได้โดยไม่ต้องพึ่งเฟรมเวิร์ก
Adobe Spectrum — ใช้ Web Components (Spectrum Web Components) เป็นดีไซน์ระบบที่ใช้ข้ามผลิตภัณฑ์และข้ามเฟรมเวิร์กของ Adobe ได้โดยไม่ผูกกับ React หรือ Vue