TL

BroadcastChannelとタブ間メッセージングの仕組み

ログイン状態やテーマを全タブへ一発で同期できる。BroadcastChannelの同報の仕組みと、postMessage/StorageEventとの違い、配送順序や対象範囲の保証を内部動作から正確に整理します。

応用BroadcastChannelpostMessageStorageEventタブ間通信同一オリジンブラウザ最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.BroadcastChannelは同一オリジンの全コンテキスト(タブ・iframe・Worker)へ、宛先を指定せず同じ名前のチャネル宛にメッセージを同報するバス型APIで、送信元自身には届かない。
  • 2.ペイロードは構造化複製でコピーされ、同一エージェントクラスタ内では送信した順に各受信者へ届く(FIFO)が、受信者ごとの相対的な到着タイミングは保証されない。
  • 3.postMessageは宛先を名指しする1対1、StorageEventは書き込みの副作用通知、BroadcastChannelは目的特化の1対多バス。用途で使い分ける。

なぜタブ間通信に専用APIが要るのか

同じサイトを2つのタブで開き、片方でログアウトしたらもう片方も即座にログアウト表示にしたい——この「同一オリジンの複数コンテキスト間で状態を揃える」要求は、Web では驚くほど面倒です。タブは互いに独立したブラウジングコンテキストで、JavaScript のヒープも実行スレッドも別々だからです。古典的には localStorage の書き込みで発火する StorageEvent を流用してきましたが、これは本来ストレージ変更の通知であって、メッセージング用に設計されたものではありません。BroadcastChannel は、この「同一オリジンの兄弟コンテキストへの同報(broadcast)」だけを目的に作られた専用 API です。仕組みを正確に押さえると、配送範囲・順序・落とし穴がはっきり見通せます。

BroadcastChannelは「名前付きの共有バス」

BroadcastChannel は、宛先を名指ししないバス(bus)型の通信路です。各コンテキストは同じチャネル名を指定して BroadcastChannel を生成すると、その名前を共有する全コンテキストが繋がった1本のバスに参加します。あるコンテキストが postMessage で送ると、同じオリジン・同じチャネル名を購読している他のすべてのコンテキストmessage イベントとして届きます。

// どのタブ・iframe・Worker でも同じ
const ch = new BroadcastChannel("auth");

ch.onmessage = (e) => {
  if (e.data.type === "logout") location.reload();
};

// ログアウト時:開いている全タブへ同報
ch.postMessage({ type: "logout" });

重要なのは、参加者を事前に列挙する必要がない点です。チャネルは名前で緩く結合されており、後から開いたタブが同名チャネルを作れば自動的にバスへ加わります。接続のハンドシェイクも、相手の参照を握る必要もありません。close() を呼ぶか、コンテキストが破棄されるとバスから外れます。

送信元自身には届かない

postMessage で送ったメッセージは、その送信に使った BroadcastChannel オブジェクト自身には配送されません。つまり「自分が送ったものを自分の onmessage で受ける」ループは起きません。ただし同じタブ内で別に生成した同名チャネルには届きます。配送の単位は「タブ」ではなく「個々の BroadcastChannel インスタンス」で、送信した1つだけが除外される、と理解するのが正確です。

配送範囲を決めるのは「オリジン」と「エージェントクラスタ」

どこまで届くかは2つの境界で決まります。1つ目はオリジンです。BroadcastChannel は同一オリジン(スキーム・ホスト・ポートが一致)の中だけで通じ、https://example.comhttps://sub.example.com は別バスです。これは同一オリジンポリシーが定める信頼境界とまったく同じ線引きで、クロスオリジンに漏れることはありません。

2つ目はエージェントクラスタ(agent cluster)、すなわち同じプロセス群でグループ化される実行単位です。同一オリジンであっても、別のブラウザプロファイルやシークレットウィンドウは異なるストレージパーティション/クラスタに属するため、原則として相互に届きません。受信できるのは「同一オリジン・同一ストレージパーティション内」の、タブ・ウィンドウ・iframe・Web Worker・Service Worker といったあらゆるコンテキストです。

要素届く条件備考
オリジンスキーム/ホスト/ポートが完全一致サブドメイン違いは別バス
チャネル名文字列が完全一致名前空間として機能
パーティション同一ストレージパーティション別プロファイル/シークレットは分離
コンテキスト種別タブ・iframe・Worker いずれも可種別は問わない

ペイロードは構造化複製でコピーされる

バスへ流すデータは、参照渡しではなく構造化複製(structured clone)アルゴリズムで深くコピーされてから各受信者へ渡されます。これは structuredClone() や Worker への postMessage同じシリアライズの仕組みです。したがって ObjectArrayMapSetDateArrayBuffer などは送れますが、関数や DOM ノードを含むと DataCloneError になり、class インスタンスを送っても prototype は失われ素の Object になります。

ch.postMessage({ ids: new Set([1, 2, 3]), at: new Date() }); // OK
ch.postMessage({ cb: () => {} }); // DataCloneError(関数は複製不可)

一点だけ Worker 向け postMessage と異なるのは、転送可能オブジェクト(Transferable)を渡せないことです。BroadcastChannel の postMessage は転送リスト引数を取らず、ArrayBuffer は常に「移動」ではなく「コピー」されます。1対多の同報では「所有権を1つの受信者へ移す」という転送の意味が成立しないためで、結果として大きなバイナリを同報すると、サイズに比例した複製コストが受信者の数に応じてかかります。

配送順序の保証——FIFOだが受信者間は非同期

順序の保証は実務で最も誤解されやすい点です。仕様上、メッセージの配送は**同一エージェントクラスタ内で送信順(FIFO)**が保たれます。つまり、ある送信元が ABC の順に送れば、各受信者は必ず ABC の順で受け取り、追い越しは起きません。各受信者のイベントキューには、その順序でメッセージタスクが積まれます。

ただし保証されるのは「各受信者内での相対順序」までで、受信者どうしの到着タイミングは揃いません。タブ1が B を処理し終える前に、タブ2はまだ A を処理しているかもしれません。配送は受信側ごとに独立したタスクとしてイベントループに積まれる非同期イベントだからです。さらに複数の送信元が混在する場合の全体順序(グローバル順序)は規定されません。送信元 X の A と送信元 Y の P が、どの受信者でも同じ前後関係で届くとは限りません。

順序保証の射程

保証されるのは「単一送信元から各受信者への FIFO」のみ。保証されないのは(1)受信者間の到着の同時性、(2)複数送信元をまたいだ全体順序、(3)受信側がメッセージを処理する速さ。順序に依存する設計をするなら、メッセージ自体に連番や論理時刻(バージョン番号)を埋めて受信側で並べ直すのが堅実です。

postMessage・StorageEventとの違い

同じ「コンテキスト間でデータを動かす」でも、3者は設計思想が異なります。window.postMessage(および MessagePort)は宛先を名指しする1対1の通信で、相手の window 参照や MessagePort を握っている必要があり、その代わりクロスオリジンでも使える(受信側が origin を検証する前提)唯一の手段です。

StorageEvent は本来ストレージ変更の副作用通知です。localStorage への書き込みが、同一オリジンの他のドキュメントstorage イベントを発火させる仕組みで、Web ストレージの章の挙動に依存します。メッセージング目的に流用できますが、書いた本人のタブには発火しない、localStorage を実際に汚すので後始末が要る、同じ値を再代入しても発火しない、といった制約があり、専用バスとしては不格好です。

観点BroadcastChannelpostMessage(window/Port)StorageEvent
通信形態1対多(バス)1対1(名指し)1対多(副作用通知)
オリジン同一オリジンのみクロスオリジン可同一オリジンのみ
宛先の指定チャネル名で緩く結合相手参照が必要ストレージキー経由
データ構造化複製(転送は不可)構造化複製+転送可文字列のみ
主目的タブ間の状態同報コンテキスト間の直接通信ストレージ変更の通知
使い分けの指針

同一オリジンで「開いている全タブに同じ知らせを配りたい」なら BroadcastChannel が最短です。クロスオリジンの iframe や別ウィンドウと通信するなら window.postMessage。状態の永続化が主目的で通知は副次的なら、まず localStorage に書き、その StorageEvent を拾う構成も合理的です。永続化と同報の両方が要るなら「localStorage に保存しつつ BroadcastChannel で能動的に知らせる」併用がよく使われます。

まとめ

まとめ

BroadcastChannel は、同一オリジン・同一ストレージパーティション内の全コンテキスト(タブ・iframe・Worker)を、チャネル名で緩く束ねる1対多のバスです。送信は構造化複製でコピーされて各受信者の message イベントに届き、送信元自身のインスタンスだけは除外されます。ArrayBuffer の転送(Transferable)はできず常にコピーになります。配送順序は単一送信元から各受信者への FIFO は保証されますが、受信者間の到着同時性や複数送信元をまたいだ全体順序は保証されないため、順序が要るならメッセージへ連番を埋めます。1対1で相手を名指しし、かつクロスオリジンも越えたいなら window.postMessage、ストレージ変更の通知を兼ねたいなら StorageEvent、純粋に同一オリジンの全タブへ同報したいなら BroadcastChannel——という役割分担を押さえれば、タブ間メッセージングの設計はほぼ迷いません。

Web/フロントエンド Article

BroadcastChannelとタブ間メッセージングの仕組みを実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

BroadcastChannel

比較で見る軸

難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 6

導入後に効く点

ペイロードは構造化複製でコピーされ、同一エージェントクラスタ内では送信した順に各受信者へ届く(FIFO)が、受信者ごとの相対的な到着タイミングは保証されない。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
Web/フロントエンド
タグ数
6

判断チェックリスト

  • 自社の用途が「BroadcastChannel / postMessage」に近いか確認する。
  • 強みである「BroadcastChannelは同一オリジンの全コンテキスト(タブ・iframe・Worker)へ、宛先を指定せず同じ名前のチャネル宛にメッセージを同報するバス型APIで、送信元自身には届かない。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

BroadcastChannelpostMessageStorageEventタブ間通信同一オリジンBroadcastChannelpostMessageStorageEvent
参考: 公式情報