BroadcastChannel API ช่วยให้สคริปต์ต้นทางเดียวกันส่งข้อความถึงบริบทการท่องเว็บอื่นๆ ได้ อาจมองว่าเป็นบัสข้อความแบบง่ายๆ ที่อนุญาตให้ใช้ความหมายของ pub/sub ระหว่างหน้าต่าง/แท็บ, iframe, Web Worker และ Service Worker
ข้อมูลเบื้องต้นเกี่ยวกับ API
Broadcast Channel API เป็น API แบบง่ายที่ช่วยให้การสื่อสารระหว่างบริบทการท่องเว็บเป็นไปอย่างง่ายดายยิ่งขึ้น กล่าวคือ การสื่อสารระหว่างหน้าต่าง/แท็บ, iframe, โปรแกรมทำงานของบริการ และ Service Worker ข้อความที่โพสต์ในช่องใดช่องหนึ่งจะส่งไปยังผู้ฟังทุกคนของช่องนั้น
ตัวสร้าง BroadcastChannel
จะมีพารามิเตอร์เดียว นั่นคือชื่อแชแนล
ชื่อจะระบุช่องและปรากฏอยู่ในบริบทต่างๆ ของการท่องเว็บ
// Connect to the channel named "my_bus".
const channel = new BroadcastChannel('my_bus');
// Send a message on "my_bus".
channel.postMessage('This is a test message.');
// Listen for messages on "my_bus".
channel.onmessage = function(e) {
console.log('Received', e.data);
};
// Close the channel when you're done.
channel.close();
การส่งข้อความ
ข้อความอาจเป็นสตริงหรือสิ่งใดก็ตามที่อัลกอริทึมโคลนที่มีโครงสร้าง (สตริง, ออบเจ็กต์, อาร์เรย์, Blobs, ArrayBuffer, Map) ที่รองรับ
ตัวอย่าง - การส่ง Blob หรือไฟล์
channel.postMessage(new Blob(['foo', 'bar'], {type: 'plain/text'}));
ช่องจะไม่ประกาศไปยังช่องนั้น ดังนั้นหากคุณมี Listener onmessage
ในหน้าเดียวกับ postMessage()
ไปยังช่องเดียวกัน เหตุการณ์ message
นั้นจะไม่เริ่มทำงาน
ความแตกต่างกับเทคนิคอื่นๆ
ณ จุดนี้ คุณอาจสงสัยว่ากรณีนี้เกี่ยวข้องกับเทคนิคอื่นๆ ในการส่งผ่านข้อความ เช่น WebSockets, SharedWorkers, MessageChannel
API และ window.postMessage()
อย่างไร Broadcast Channel API ไม่ได้มาแทนที่ API เหล่านี้ แต่ละองค์ประกอบมีไว้สำหรับวัตถุประสงค์ Broadcast Channel API มีไว้เพื่อการสื่อสารแบบ 1 ต่อหลายคนได้ง่ายระหว่างสคริปต์ในต้นทางเดียวกัน
ตัวอย่างการใช้งานช่องออกอากาศมีดังนี้
- ตรวจหาการดำเนินการของผู้ใช้ในแท็บอื่นๆ
- ทราบเมื่อผู้ใช้เข้าสู่ระบบบัญชีในหน้าต่าง/แท็บอื่น
- สั่งให้ผู้ปฏิบัติงานทำงานเบื้องหลังบางอย่าง
- ทราบเมื่อบริการทำงานบางอย่างเสร็จสิ้น
- เมื่อผู้ใช้อัปโหลดรูปภาพในหน้าต่างหนึ่ง ให้ส่งต่อรูปภาพดังกล่าวไปยังหน้าอื่นๆ ที่เปิดอยู่
ตัวอย่าง - หน้าเว็บที่ทราบเมื่อผู้ใช้ออกจากระบบ แม้ว่าจะมาจากแท็บอื่นที่เปิดอยู่ในเว็บไซต์เดียวกัน เช่น
<button id="logout">Logout</button>
<script>
function doLogout() {
// update the UI login state for this page.
}
const authChannel = new BroadcastChannel('auth');
const button = document.querySelector('#logout');
button.addEventListener('click', e => {
// A channel won't broadcast to itself so we invoke doLogout()
// manually on this page.
doLogout();
authChannel.postMessage({cmd: 'logout', user: 'Eric Bidelman'});
});
authChannel.onmessage = function(e) {
if (e.data.cmd === 'logout') {
doLogout();
}
};
</script>
ในอีกตัวอย่างหนึ่ง สมมติว่าคุณต้องการสั่งให้ Service Worker นำเนื้อหาที่แคชไว้ออกหลังจากที่ผู้ใช้เปลี่ยน "การตั้งค่าพื้นที่เก็บข้อมูลออฟไลน์" ในแอปของคุณ คุณสามารถลบแคชของผู้ใช้ได้โดยใช้ window.caches
แต่โปรแกรมทำงานของบริการอาจมียูทิลิตี้สำหรับการดำเนินการดังกล่าวอยู่แล้ว เราสามารถใช้ Broadcast Channel API เพื่อนำโค้ดนั้น
มาใช้ซ้ำได้! หากไม่มี Broadcast Channel API คุณจะต้องวนผลลัพธ์ของ self.clients.matchAll()
และเรียกใช้ postMessage()
บนไคลเอ็นต์แต่ละรายเพื่อให้สื่อสารจากโปรแกรมทำงานของบริการไปยังไคลเอ็นต์ทั้งหมดของลูกค้าได้ (โค้ดจริงที่ทำเช่นนั้น) การใช้ช่องออกอากาศจะทำให้ช่องนี้เป็น O(1)
แทน O(N)
ตัวอย่าง - สั่งให้ Service Worker นำแคชออกโดยนำวิธีการยูทิลิตีภายในกลับมาใช้ซ้ำ
ใน index.html
const channel = new BroadcastChannel('app-channel');
channel.onmessage = function(e) {
if (e.data.action === 'clearcache') {
console.log('Cache removed:', e.data.removed);
}
};
const messageChannel = new MessageChannel();
// Send the service worker a message to clear the cache.
// We can't use a BroadcastChannel for this because the
// service worker may need to be woken up. MessageChannels do that.
navigator.serviceWorker.controller.postMessage({
action: 'clearcache',
cacheName: 'v1-cache'
}, [messageChannel.port2]);
ใน sw.js
function nukeCache(cacheName) {
return caches.delete(cacheName).then(removed => {
// ...do more stuff (internal) to this service worker...
return removed;
});
}
self.onmessage = function(e) {
const action = e.data.action;
const cacheName = e.data.cacheName;
if (action === 'clearcache') {
nukeCache(cacheName).then(removed => {
// Send the main page a response via the BroadcastChannel API.
// We could also use e.ports[0].postMessage(), but the benefit
// of responding with the BroadcastChannel API is that other
// subscribers may be listening.
const channel = new BroadcastChannel('app-channel');
channel.postMessage({action, removed});
});
}
};
ความแตกต่างกับ postMessage()
สิ่งที่แตกต่างจาก postMessage()
คือคุณไม่จำเป็นต้องดูแลรักษาการอ้างอิงไปยัง iframe หรือผู้ปฏิบัติงานเพื่อติดต่อสื่อสารกับ iframe อีกต่อไป ดังนี้
// Don't have to save references to window objects.
const popup = window.open('https://another-origin.com', ...);
popup.postMessage('Sup popup!', 'https://another-origin.com');
window.postMessage()
ยังช่วยให้คุณสื่อสารข้ามต้นทางได้ด้วย Broadcast Channel API มาจากแหล่งเดียวกัน เนื่องจากข้อความมีการรับประกันว่ามาจากต้นทางเดียวกัน จึงไม่มีความจำเป็นที่จะต้องตรวจสอบอีเมลเหมือนที่เคยทำกับ window.postMessage()
// Don't have to validate the origin of a message.
const iframe = document.querySelector('iframe');
iframe.contentWindow.onmessage = function(e) {
if (e.origin !== 'https://expected-origin.com') {
return;
}
e.source.postMessage('Ack!', e.origin);
};
เพียง "ติดตาม" ช่องที่ต้องการและสื่อสารแบบ 2 ทิศทางได้อย่างปลอดภัย
ความแตกต่างกับ SharedWorker
ใช้ BroadcastChannel
สำหรับกรณีง่ายๆ ที่คุณต้องส่งข้อความไปยังหน้าต่าง/แท็บ หรือผู้ปฏิบัติงานหลายรายการ
สำหรับกรณีการใช้งานที่แปลกใหม่กว่า เช่น การจัดการล็อก สถานะที่แชร์ การซิงค์ทรัพยากรระหว่างเซิร์ฟเวอร์กับไคลเอ็นต์หลายเครื่อง หรือการแชร์การเชื่อมต่อ WebSocket กับโฮสต์ระยะไกล ผู้ปฏิบัติงานที่ใช้งานร่วมกันคือโซลูชันที่เหมาะสมที่สุด
ความแตกต่างกับ MessageChannel API
ความแตกต่างที่สำคัญระหว่าง Channel Messaging API กับ BroadcastChannel
คือวิธีแบบหลังเป็นวิธีที่จะส่งข้อความถึงผู้ฟังหลายราย (1 ต่อหลายคน) MessageChannel
มีไว้เพื่อการสื่อสารแบบหนึ่งต่อหนึ่งโดยตรงระหว่างสคริปต์ ทั้งยังมีความซับซ้อนมากขึ้น ทำให้คุณต้องตั้งค่าช่องโดยใช้พอร์ตที่แต่ละฝั่ง
การตรวจหาฟีเจอร์และการรองรับเบราว์เซอร์
ในปัจจุบัน Chrome 54, Firefox 38 และ Opera 41 สนับสนุน Broadcast Channel API
if ('BroadcastChannel' in self) {
// BroadcastChannel API supported!
}
โพลีฟิลมี 2-3 รายการให้บริการ ดังนี้
- https://gist.github.com/alexis89x/041a8e20a9193f3c47fb
- https://gist.github.com/inexorabletash/52f437d1451d12145264
ยังไม่เคยลองวิธีนี้ ระยะทางอาจแตกต่างออกไป