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番目のウィンドウを更新してタイトルを更新する必要があります。

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

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

それが機能する場所

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

これは、からメッセージをブロードキャストできることを意味します 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()

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