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

BroadcastChannel

BroadcastChannel เป็น browser API ที่ให้ message bus แบบ publish/subscribe ที่ scoped ไว้กับ origin เดียวกัน แท็บ, iframe หรือ worker ใดก็ตามที่สร้าง channel ด้วยชื่อเดียวกันจะเข้าร่วมกลุ่มเดียวกันโดยอัตโนมัติ เมื่อสมาชิกหนึ่งเรียก postMessage สมาชิกอื่นทุกคนจะได้รับข้อความผ่าน handler onmessage

มีความคล้ายคลึงกับ storage event ของ localStorage แต่มีความแตกต่างที่สำคัญ:

คุณสมบัติBroadcastChannelstorage event
เนื้อหาข้อความค่าใดก็ได้ที่ structured-cloneableString เท่านั้น (storage value)
ทำงานใน workersใช่ (Shared/Service Workers)ไม่
เกิดในแท็บที่ส่งไม่ไม่
ต้องเขียน localStorage ก่อนไม่ — ส่งตรงใช่
// Create (or join) a channel by name
const ch = new BroadcastChannel('my-channel');
// Listen for messages
ch.onmessage = (event) => {
console.log('Got:', event.data);
};
// Send a message to all other members
ch.postMessage({ type: 'UPDATE', payload: 42 });
// When done, release resources
ch.close();

ชื่อเป็นตัวระบุเพียงอย่างเดียว — สองหน้าบน origin เดียวกันที่ใช้ new BroadcastChannel('chat') จะเป็น peer กันโดยอัตโนมัติ ไม่มีขั้นตอน server หรือการลงทะเบียน

ใช้ addEventListener แทน onmessage ก็ได้ ทำให้แนบ listener หลายตัวได้:

const ch = new BroadcastChannel('notifications');
ch.addEventListener('message', (e) => {
console.log('Handler 1:', e.data);
});
ch.addEventListener('message', (e) => {
console.log('Handler 2:', e.data);
});

เรียก ch.close() เมื่อไม่ต้องการรับข้อความอีก channel ไม่ใช่ shared state — การปิดในแท็บหนึ่งไม่มีผลต่อแท็บอื่น

const ch = new BroadcastChannel('demo:temp');
// ... use it ...
ch.close(); // releases internal resources

ตัวอย่างด้านล่างสร้าง channel แนบ listener ส่งข้อความ และปิดหลังรับข้อความ เพราะ sender และ receiver อยู่ในหน้าเดียวกัน ข้อความจึงสะท้อนกลับ — ในแอปจริง listener อยู่ในแท็บแยก

Browser Storage
ตัวเลือกBenefitCost
BroadcastChannelส่งข้อความแบบ real-time ไปยังทุกแท็บ/worker ที่ join channel เดียวกัน โดยไม่ต้องเขียนลง storage ก่อนไม่มี built-in persistence — ถ้าไม่มีแท็บใด listen อยู่ตอนส่ง ข้อความจะหายไปเลย
storage eventใช้ persistence layer ที่มีอยู่แล้ว (localStorage) เป็น side effect โดยไม่ต้องตั้ง channel เพิ่มส่งได้แค่ string และไม่ยิงในแท็บที่เขียนเอง ต้อง serialize/deserialize เอง
window.postMessage ข้าม window referenceควบคุม target ได้ละเอียด ส่งเฉพาะ window ที่มี reference อยู่ต้องถือ reference ของแต่ละ window ไว้เอง ใช้ยากเมื่อมีหลายแท็บที่เปิด-ปิดแบบไดนามิก
  • คิดว่า BroadcastChannel เก็บข้อความไว้รอผู้รับที่ join ทีหลัง — ต้อง join channel ก่อนส่งข้อความเสมอ ถ้าแท็บผู้รับยังไม่ได้สร้าง BroadcastChannel ในตอนที่ส่ง ข้อความนั้นจะหายไปเงียบๆ
  • ลืมเรียก ch.close() เมื่อ component unmount — channel object และ listener จะค้างอยู่ใน memory กลายเป็น leak โดยเฉพาะใน SPA ที่ mount/unmount บ่อย
  • คาดหวังว่าแท็บที่เรียก postMessage จะได้รับ event ของตัวเองด้วย — BroadcastChannel ไม่ส่งข้อความกลับไปยัง sender เหมือนกับที่ storage event ก็ไม่ยิงในแท็บที่เขียน ต้องอัปเดต UI ของแท็บที่ส่งเองแยกต่างหาก

💡 ตัวอย่างจากของจริง

Google Docs — ใช้ pattern คล้าย BroadcastChannel เพื่อ sync สถานะการแก้ไข เช่น cursor, selection และ presence indicator ข้ามแท็บที่เปิดเอกสารเดียวกัน

Slack web — ใช้ cross-tab messaging เพื่อให้ notification badge และ unread count sync กันทุกแท็บ โดยไม่ต้อง poll server ซ้ำในแต่ละแท็บ

ส่งค่าประเภทใดได้บ้างผ่าน BroadcastChannel.postMessage()?
สองแท็บบน https://example.com ต่างสร้าง new BroadcastChannel("sync") แท็บ A เรียก postMessage แท็บใดได้รับ message event?
เกิดอะไรขึ้นเมื่อเรียก ch.close() ในแท็บหนึ่ง?
ข้อได้เปรียบสำคัญของ BroadcastChannel เหนือ storage event ของ localStorage คืออะไร?