BroadcastChannel API จะอนุญาตให้สคริปต์ต้นทางเดียวกันส่งข้อความไปยังบริบทการท่องเว็บอื่นๆ ได้ โดยอาจมองว่าเป็นบัสข้อความง่ายๆ ที่อนุญาตให้ใช้ความหมาย pub/sub ระหว่างหน้าต่าง/แท็บ, iframe, Web Worker และ Service Worker
ข้อมูลเบื้องต้นเกี่ยวกับ API
Broadcast Channel API เป็น API แบบง่ายที่ทำให้การติดต่อสื่อสารระหว่างบริบทต่างๆ ในการท่องเว็บง่ายขึ้น ซึ่งก็คือการสื่อสารระหว่างหน้าต่าง/แท็บ, iframe, Web Worker และ 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, แผนที่)
ตัวอย่าง - การส่ง 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>
อีกตัวอย่างหนึ่ง สมมติว่าคุณต้องการสั่งให้โปรแกรมทำงานของบริการนำ
เนื้อหาที่แคชไว้หลังจากที่ผู้ใช้เปลี่ยน "การตั้งค่าพื้นที่เก็บข้อมูลออฟไลน์" ในแอปของคุณ
คุณลบแคชของผู้ใช้ได้โดยใช้ window.caches
แต่ Service Worker อาจ
มียูทิลิตีในการทำงานนี้อยู่แล้ว เราสามารถใช้ Broadcast Channel API เพื่อ
ใช้รหัสนั้นซ้ำ! หากไม่ได้ใช้ Broadcast Channel API คุณต้องวนซ้ำผลลัพธ์ของ self.clients.matchAll()
และเรียกใช้ postMessage()
ในไคลเอ็นต์แต่ละรายการเพื่อสื่อสารจาก Service Worker ไปยังไคลเอ็นต์ทั้งหมด (โค้ดจริงที่ทำหน้าที่ดังกล่าว) การใช้ช่องสำหรับออกอากาศจะทำให้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
มีไว้สำหรับการสื่อสารแบบตัวต่อตัวระหว่างสคริปต์โดยตรง และยังเกี่ยวข้องกว่า โดยคุณจะต้องตั้งค่าช่องโดยมีพอร์ตอยู่ปลายทั้ง 2 ด้าน
การตรวจหาฟีเจอร์และการสนับสนุนเบราว์เซอร์
ในปัจจุบัน Chrome 54, Firefox 38 และ Opera 41 รองรับ Broadcast Channel API
if ('BroadcastChannel' in self) {
// BroadcastChannel API supported!
}
สำหรับ Polyfill นั้นมีวิธีการต่างๆ ดังนี้
- https://gist.github.com/alexis89x/041a8e20a9193f3c47fb
- https://gist.github.com/inexorabletash/52f437d1451d12145264
เรายังไม่ได้ลองใช้ ระยะไมล์ของคุณอาจต่างจากนี้