BroadcastChannel APIは、同じオリジンの異なるウィンドウ/タブ/iframe間で通信できる新しいWebプラットフォームAPIです。 BroadcastChannelを使用すると、派手で気が遠くなるように聞こえるかもしれませんが、非常に簡単で便利です。

BroadcastChannelAPIを使用する理由

お気に入りのウェブサイトの1つにログインしてみてください( youtube.com で試しました)。 次に、同じWebサイトを別のタブで開きます。 通常、両方のページにログインします。 次に、タブの1つからログアウトします。 ほとんどのサイトでは、一方のページにログインし、もう一方のページからログオフしたように見えます。

ウィンドウは、ログイン状態とログアウト状態のさまざまな状態にあります。 それは素晴らしいことではなく、あなたがマニアックなタバー(私のように)である場合、それはいくつかの混乱につながる可能性があります。

これはセキュリティの問題になることさえあります。 ユーザーが会社のダッシュボードを使用して喫茶店にいると想像してください。 彼はトイレ休憩を取るためにログオフし、コンピューターの電源を入れたままにします。 アプリケーションが複数のタブで開かれた場合、他のタブで利用可能なデータにアクセスできます(画面上またはJWTトークンの場合もあります)。

BroadcastChannelAPIコード

これは、ローカルHTMLファイルにコピーアンドペーストできる非常に簡単な例です。

<!DOCTYPE html>

<body>
  <!-- The title will change to greet the user -->
  <h1 id="title">Hey</h1>
  <input id="name-field" placeholder="Enter Your Name"/>
</body>

<script>

var bc = new BroadcastChannel('gator_channel');

(()=>{
  const title = document.getElementById('title');
  const nameField = document.getElementById('name-field');
  const setTitle = (userName) => {
    title.innerHTML = 'Hey ' + userName;
  }

  bc.onmessage = (messageEvent) => {
    // If our broadcast message is 'update_title' then get the new title from localStorage
    if (messageEvent.data === 'update_title') {
      // localStorage is domain specific so when it changes in one window it changes in the other
      setTitle(localStorage.getItem('title'));
    }
  }

  // When the page loads check if the title is in our localStorage
  if (localStorage.getItem('title')) {
    setTitle(localStorage.getItem('title'));
  } else {
    setTitle('please tell us your name');
  }

  nameField.onchange = (e) => {
    const inputValue = e.target.value;
    // In the localStorage we set title to the user's input
    localStorage.setItem('title', inputValue);
    // Update the title on the current page 
    setTitle(inputValue);
    // Tell the other pages to update the title
    bc.postMessage('update_title');
  }
})()
</script>

このページにはタイトルと入力があります。 ユーザーが入力に自分の名前を入力すると、userNameキーの下のローカルストレージに名前が保存されます。 次に、アプリケーションのタイトルを'Hey' + userNameに設定します。 したがって、ユーザーの名前がSarahの場合、ページには「HeySarah」と表示されます。

ユーザーが一方のウィンドウに自分の名前を入力したときにBroadcastChannelがなかった場合、もう一方のウィンドウは更新されません。 コードにブロードキャストチャネルがないと、ユーザーは2番目のウィンドウを更新してタイトルを更新する必要があります。

したがって、最初の行では、「gator_channel」というBroadcastChannelを作成します。 次に、onmessageメソッドを使用してメッセージレシーバーを作成します。 onmessageを1つの引数を取る関数(別名イベントメッセージ)に設定します。 次に、コードで、メッセージの名前がupdate_titleであることを確認します。 その場合、ローカルストレージからユーザー名を抽出します。

放送チャンネルでpostMessageを呼び出すと、他のウィンドウではonmessageが呼び出されます。 したがって、ウィンドウ1にジャックを入力すると、ウィンドウ1はbc.postMessage('updated_title')を呼び出します。 これにより、ウィンドウ2および同じ原点で開いている他のウィンドウでonmessageがアクティブになります。

それが機能する場所

window.postMessageなどの他のAPIとは異なり、開いている他のウィンドウやタブについて何も知る必要はありません。 ブロードキャストチャネルは、同じオリジン(同じスキーム、ホスト、およびポート)にあるすべてのタブまたはウィンドウで機能します。

これは、https://alligator.io/からhttps://alligator.io/js/broadcast-channelsにメッセージをブロードキャストできることを意味します。 必要なのは、同じチャネル名を使用して両方のページにBroadcastChanelオブジェクトを配置することだけです。

const bc = new BroadcastChannel('alligator_channel');
bc.onmessage = (eventMessage) => {
  // do something different on each page
}

ホストが異なる場合、それは機能しません:

ポートが異なる場合は機能しません。

スキームが異なる場合は機能しません。 これは、httpとhttpsがそれぞれポート80と443を使用するという標準であるため、さまざまなポートに似ています。

ウィンドウの1つがシークレットモードであるか、ブラウザ間である場合、ブロードキャストチャネルは機能しません(例: FirefoxからChromeへ)。

ブラウザの互換性

caniuse.com )[ https://caniuse.com/#feat=broadcastchannel] によると、BroadcastChannelAPIは約75.6% ofのユーザーが利用できます。 SafariとInternetExplorerはまだサポートしていません。

私の知る限り、最も人気のあるポリフィルはこれです。 BroadcastChannelAPIとほぼ同じように使用できます。 BroadcastChannel APIが利用可能であることを検出すると、より高速な結果を得るために自動的に使用されます。 それ以外の場合は、IndexedDBまたはLocalStorageを使用します。

どのようなメッセージを渡すことができますか?

構造化クローンアルゴリズムを使用してクローンできるものはすべて渡すことができます。 これには、記号を除くほとんどすべてが含まれます。

  • シンボル(ブール、ヌル、未定義、数値、BigInt、文字列)を除くすべてのプリミティブ型
  • ブールオブジェクトと文字列オブジェクト
  • 日付
  • 正規表現
  • ブロブ
  • ファイル、ファイルリスト
  • ArrayBuffers、ArrayBufferViews
  • ImageBitmaps、ImageDatas
  • 配列、オブジェクト、マップ、セット

シンボルのようなものを送信しようとすると、送信側でエラーが発生します。


コードを更新して、文字列の代わりにオブジェクトを使用しましょう。

  bc.onmessage = (messageEvent) => {
    const data = messageEvent.data
    // If our broadcast message is 'update_title' then get the new title from localStorage
    switch (data.type) {
      case 'update_title':
        if (data.title){
          setTitle(data.title);
        }
        else
          setTitle(localStorage.getItem('title'));
        break
      default:
        console.log('we received a message')
    }
  };
  // ... Skipping Code
  bc.postMessage({type: 'update_title', title: inputValue});

放送チャンネルでできること

私たちが想像できることはたくさんあります。 最も明白なユースケースは、状態を共有することです。 たとえば、FluxやReduxなどを使用して状態を管理している場合は、メッセージをブロードキャストして、ストアがタブ間で同じままになるようにすることができます。 ステートマシンに似たものを構築することも想像できます。

大きなファイルをさまざまな形式で送信することもできることを確認しました。 タブ間で画像などの大きなファイルを共有することで、帯域幅をいくらか節約できる可能性があります。

放送チャンネルを閉じる

放送チャンネルを閉じるのはとても簡単です。 ブロードキャストチャネルでcloseメソッドを実行するだけです。

bc.close()

アプリケーションの状態に応じて、チャネルを閉じたり開いたりすることができます。 たとえば、ログインしているときに、アプリケーションの状態を共有するための特定のチャネルがある場合があります。 ログアウトするときに、そのチャネルを閉じることをお勧めします。