BroadcastChannel API 可讓同源指令碼將訊息傳送至其他瀏覽內容。可以將其視為簡單的訊息匯流排,可在視窗/分頁、iframe、Web worker 和服務 worker 之間實現 pub/sub 語意。
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();
傳送訊息
訊息可以是字串,或 結構化複製演算法支援的任何內容 (字串、物件、陣列、Blob、ArrayBuffer、地圖)。
範例:傳送 Blob 或檔案
channel.postMessage(new Blob(['foo', 'bar'], {type: 'plain/text'}));
管道不會向自身進行廣播。因此,如果您在同一頁面上有 onmessage
事件監聽器,而 postMessage()
則為相同管道的事件,則不會觸發 message
事件。
與其他技術的差異
您可能會好奇,這與其他訊息傳遞技術 (例如 WebSockets、SharedWorkers、MessageChannel
API 和 window.postMessage()
) 有何關聯。Broadcast Channel API 不會取代這些 API。每個都有不同的用途。Broadcast Channel API 可讓同源的程式碼之間輕鬆進行一對多通訊。
廣播管道的部分用途:
- 偵測其他分頁中的使用者動作
- 得知使用者在其他視窗/分頁中登入帳戶的時間。
- 指示 worker 執行一些背景工作
- 瞭解服務完成某項動作的時間。
- 當使用者在一個視窗中上傳相片時,請將相片傳送至其他已開啟的頁面。
範例:網頁會在使用者登出時通知您,即使是從同一個網站的其他分頁登出也一樣:
<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
刪除快取,但服務工作者可能已包含用於執行此操作的工具。我們可以使用 Broadcast Channel API 重複使用該程式碼!如果沒有 Broadcast Channel API,您必須循環 self.clients.matchAll()
的結果,並在每個用戶端上呼叫 postMessage()
,才能實現服務工作者與其所有用戶端之間的通訊 (實際執行這項操作的程式碼)。使用廣播管道會將此值設為 O(1)
,而非 O(N)
。
範例:指示服務工作站移除快取,並重複使用其內部公用程式方法。
在 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 或 worker 的參照,才能與其通訊:
// 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);
};
只要「訂閱」特定管道,就能進行安全的雙向通訊!
與 SharedWorkers 的差異
在需要將訊息傳送至多個視窗/分頁或 worker 的簡單情況下,請使用 BroadcastChannel
。
對於管理鎖定、共用狀態、在伺服器和多個用戶端之間同步處理資源,或是與遠端主機共用 WebSocket 連線等較複雜的用途,共用 worker 是最合適的解決方案。
與 MessageChannel API 的差異
Channel Messaging API 與 BroadcastChannel
的主要差異在於,後者是將訊息分派給多個監聽器的一種方式 (一對多)。MessageChannel
是用於直接在指令碼之間進行一對一通訊。這項方法也較為複雜,因為您必須為通道設定端點通訊埠。
功能偵測和瀏覽器支援
目前 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
我沒有嘗試過這些,因此你的里程數可能會有所不同。