BroadcastChannel API - 網路訊息匯流排

Eric Bidelman

BroadcastChannel API 允許相同來源指令碼傳送訊息至其他瀏覽環境。這可以視為一個簡單的訊息匯流排,這樣就能在 Windows/tab、iframe、Web Worker 和 Service 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 事件不會觸發。

與其他技術的差異

現階段,您可能會好奇,這與 WebSocket、SharedWorkers、MessageChannel APIwindow.postMessage() 等訊息傳遞的其他技術有何關係。Broadcast Channel API 不會取代這些 API。兩者各有其用途。Broadcast Channel API 可讓您在相同來源的指令碼之間輕鬆進行一對多通訊。

廣播頻道的使用案例如下:

  • 偵測其他分頁中的使用者動作
  • 瞭解使用者何時在其他視窗/分頁中登入帳戶。
  • 指示工作站執行一些背景工作
  • 瞭解服務何時執行了某些動作。
  • 使用者在一個視窗上傳相片時,將其傳送到其他開啟的頁面。

範例 - 知道使用者何時登出的網頁,即使是在同一個網站上開啟的分頁也一樣:

<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 的差異

當您需要向潛在多個視窗/分頁或工作站傳送訊息時,請使用 BroadcastChannel

對於想管理鎖定、共用狀態、在伺服器與多個用戶端之間同步處理資源,或是與遠端主機共用 WebSocket 連線等應用方式較強,共用工作者是最適合的解決方案。

與 MessageChannel API 的差異

Channel Messaging APIBroadcastChannel 之間的主要差異在於,後者是將訊息分派給多個事件監聽器 (一對多) 的方法。MessageChannel 適用於指令碼之間的一對一通訊。這種做法更為複雜,廣告客戶也需要在兩端設定通訊埠,

功能偵測和瀏覽器支援

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

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

對於 polyfill,以下是幾個:

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

資源