BroadcastChannel API - ウェブ用のメッセージバス

BroadcastChannel API を使用すると、同一オリジンのスクリプトで他のブラウジング コンテキストにメッセージを送信できます。これは、ウィンドウ/タブ、iframe、ウェブ ワーカー、Service Worker の間で Pub/Sub セマンティクスを可能にするシンプルなメッセージバスと考えることができます。

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();

メッセージを送信する

メッセージには、文字列または構造化クローン アルゴリズムでサポートされているものを使用できます(文字列、オブジェクト、配列、blob、ArrayBuffer、Map)。

- Blob または File を送信する

channel.postMessage(new Blob(['foo', 'bar'], {type: 'plain/text'}));

チャンネルが自身にブロードキャストされることはありません。たとえば、onmessage リスナーと 同じチャンネルへの postMessage() と同じページに掲載した場合、その message イベント 呼び出されることはありません。

他の手法との違い

この時点で、WebSocket、SharedWorker、MessageChannel APIwindow.postMessage() などのメッセージ受け渡しの他の手法と、これがどう関係するのか疑問に思うかもしれません。Broadcast Channel API はこれらの API に代わるものではありません。それぞれが目的を果たします。Broadcast Channel API は、同じオリジンのスクリプト間で簡単に 1 対多の通信を行うためのものです。

ブロードキャスト チャンネルのユースケースには、次のようなものがあります。

  • 他のタブでのユーザー操作を検出する
  • ユーザーが別のウィンドウやタブでアカウントにログインしたタイミングを把握する。
  • ワーカーにバックグラウンド処理を行うよう指示する
  • サービスがなんらかのアクションの実行をいつ完了したかを知る。
  • ユーザーが 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 を使用してキャッシュを削除することもできますが、Service Worker で これを行うためのユーティリティがすでに含まれています。Broadcast Channel API を使用すると、 再利用できます。Broadcast Channel API を使用しない場合は、Service Worker からすべてのクライアントへの通信を実現するために、self.clients.matchAll() の結果をループして各クライアントで postMessage() を呼び出す必要があります(そのための実際のコード)。ブロードキャスト チャンネルを使用すると、O(N) ではなく O(1) になります。

- 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 またはワーカーとやり取りするために参照を維持する必要がなくなりました。

// 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 の主な違いは、後者は複数のリスナー(1 対多)にメッセージをディスパッチする手段である点です。MessageChannel は、スクリプト間での直接の 1 対 1 の通信用です。また、より複雑で、両端にポートを持つチャネルを設定する必要があります。

機能検出とブラウザ サポート

現在、Chrome 54、Firefox 38、Opera 41 では Broadcast Channel API がサポートされています。

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

ポリフィルには以下のようなものがあります。

私はこれらを試したことがないので、あなたの距離は異なるかもしれません。

リソース