ข้ามไปยังเนื้อหา

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 ของเฟรมเวิร์ก (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 ที่มีมาในตัวทุกประการ

เอนจินเรนเดอร์หลักทั้งสี่ตัวรองรับสเปก 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 คือ 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 โดยอัตโนมัติ

ตัวเลือกBenefitCost
Web Components (มาตรฐานเนทีฟ)ไม่ผูกกับเฟรมเวิร์กใด ใช้งานได้ทุกที่ที่มี browser ไม่ต้อง transpile หรือมี runtime libraryDX สู้เฟรมเวิร์กอย่าง 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

ข้อใดต่อไปนี้อธิบาย custom element ได้ถูกต้อง?
browser ตัวใดที่ไม่รองรับ customized built-in elements?
ข้อได้เปรียบหลักของ Web Components เหนือ component ของเฟรมเวิร์กคืออะไร?
หลังจากเรียก customElements.define แล้ว tag ที่ตรงกับชื่อซึ่งมีอยู่แล้วในเอกสารจะเกิดอะไรขึ้น?