BroadcastChannel API - 網路訊息匯流排

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 APIwindow.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 的參照,就能與 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 APIBroadcastChannel 的主要差異在於,後者是將訊息分派給多個監聽器的一種方式 (一對多)。MessageChannel 適用於指令碼之間的一對一通訊,這項方法也較為複雜,因為您必須為通道設定端點通訊埠。

功能偵測和瀏覽器支援

目前 Chrome 54、Firefox 38 和 Opera 41 支援 Broadcast Channel API。

if ('BroadcastChannel' in self) {
    // BroadcastChannel API supported!
}

至於 polyfill,目前有以下幾種:

我還沒試過這些方法,因此你的里程數可能會有所不同。

資源