web-dev-qa-db-ja.com

タブまたはウィンドウ間の通信

私は、トレースを残すことなく、ブラウザ内の複数のタブまたはウィンドウ(CORSではなく同じドメイン上)の間で通信する方法を探していました。いくつかの解決策がありました。

  1. ウィンドウオブジェクトを使用
  2. postMessage
  3. cookies
  4. localStorage

最初の方法はおそらく最悪の解決策です。現在のウィンドウからウィンドウを開く必要があり、その後はウィンドウを開いたままにしておけば通信できます。いずれかのウィンドウでページをリロードすると、おそらく通信が失われます。

PostMessageを使用する2番目のアプローチは、おそらくオリジン間の通信を可能にしますが、最初のアプローチと同じ問題を抱えています。ウィンドウオブジェクトを維持する必要があります。

3番目の方法は、Cookieを使用して、同じドメインのすべてのウィンドウにメッセージを送信するように見えるデータをブラウザに保存しますが、問題は、すべてのタブが既に「メッセージ」を読んでいるかどうかがわからないことです清掃。 Cookieを定期的に読み取るには、何らかのタイムアウトを実装する必要があります。さらに、Cookieの最大長である4KBによって制限されます。

LocalStorageを使用する4番目のソリューションは、Cookieの制限を克服するように思われ、イベントを使用してリッスンすることさえできます。使用方法は、受け入れられた回答に記載されています。

2018年の編集:受け入れられた回答は引き続き機能しますが、BroadcastChannelを使用する最新のブラウザー用の新しいソリューションがあります。 BroadcastChannelを使用してタブ間でメッセージを簡単に送信する方法を説明する簡単な例については、他の回答を参照してください。

124
Tomas M

2018年編集:この目的のためにBroadcastChannelを使用した方が良いかもしれません。以下の他の回答を参照してください。しかし、タブ間の通信にlocalstorageを使用したい場合は、次のようにします:

タブが他のタブにメッセージを送信したときに通知を受けるには、単に「ストレージ」イベントにバインドする必要があります。すべてのタブで、これを行います:

$(window).on('storage', message_receive);

関数message_receiveは、他のタブでlocalStorageの値を設定するたびに呼び出されます。イベントリスナーには、localStorageに新しく設定されたデータも含まれているため、localStorageオブジェクト自体を解析する必要さえありません。これは、設定した直後に値をリセットして、トレースを効果的にクリーンアップできるため、非常に便利です。メッセージングの機能は次のとおりです。

// use local storage for messaging. Set message in local storage and clear it right away
// This is a safe way how to communicate with other tabs while not leaving any traces
//
function message_broadcast(message)
{
    localStorage.setItem('message',JSON.stringify(message));
    localStorage.removeItem('message');
}


// receive message
//
function message_receive(ev)
{
    if (ev.originalEvent.key!='message') return; // ignore other keys
    var message=JSON.parse(ev.originalEvent.newValue);
    if (!message) return; // ignore empty msg or msg reset

    // here you act on messages.
    // you can send objects like { 'command': 'doit', 'data': 'abcd' }
    if (message.command == 'doit') alert(message.data);

    // etc.
}

したがって、タブがonstorageイベントにバインドされ、これらの2つの関数を実装したら、次のように呼び出している他のタブに単純にメッセージをブロードキャストできます。

message_broadcast({'command':'reset'})

まったく同じメッセージを2回送信しても1回しか伝達されないため、メッセージを繰り返す必要がある場合は、次のように一意の識別子を追加してください。

message_broadcast({'command':'reset', 'uid': (new Date).getTime()+Math.random()})

また、メッセージをブロードキャストする現在のタブは実際には受信せず、同じドメイン上の他のタブまたはウィンドウのみを受信することに注意してください。

RemoveItem()の前にsetItem()を呼び出した直後に、ユーザーが別のWebページをロードするか、タブを閉じるとどうなるかを尋ねることができます。さて、私自身のテストから、ブラウザーは関数message_broadcast()全体が完了するまでアンロードを保留にします。非常に長いfor()サイクルを入れるようにテストしましたが、クローズする前にサイクルが完了するのを待っていました。ユーザーがその間でタブを強制終了すると、ブラウザはメッセージをディスクに保存するのに十分な時間を持っていないため、このアプローチはトレースなしでメッセージを送信する安全な方法のように思えます。コメントを歓迎します。

120
Tomas M

この目的専用の最新のAPIがあります- ブロードキャストチャネル

次のように簡単です。

var bc = new BroadcastChannel('test_channel');

bc.postMessage('This is a test message.'); /* send */

bc.onmessage = function (ev) { console.log(ev); } /* receive */

メッセージが単なるDOMStringである必要はなく、あらゆる種類のオブジェクトを送信できます。

おそらく、APIのクリーンさは別として、このAPIの主な利点は、オブジェクトの文字列化がないことです。

現在 サポート はChromeおよびFirefoxでのみ使用できますが、localStorageを使用するポリフィルを見つけることができます。

79
user

JQueryに基づいていないソリューションを検索する場合、これはThomas Mが提供するソリューションの単純なJavaScriptバージョンです。

window.addEventListener("storage", message_receive);

function message_broadcast(message) {
    localStorage.setItem('message',JSON.stringify(message));
}

function message_receive(ev) {
    if (ev.key == 'message') {
        var message=JSON.parse(ev.newValue);
    }
}
32
Nacho Coloma

チェックアウト AcrossTabs -クロスオリジンブラウザタブ間の簡単な通信postMessagesessionStorage APIの組み合わせを使用して、通信をより簡単で信頼性の高いものにします。


さまざまなアプローチがあり、それぞれに長所と短所があります。それぞれについて説明しましょう:

  1. LocalStorage

    長所

    1. Webストレージは、Cookieの改善として単純に見ることができ、はるかに大きなストレージ容量を提供します。 Mozillaのソースコードを見ると、5120KB5MB250万文字(Chromeでは250万文字))は、ドメイン全体のデフォルトのストレージサイズです。これにより、通常の4KB Cookieよりもかなり多くのスペースを使用できます。
    2. データは、HTTP要求(HTML、画像、JavaScript、CSSなど)ごとにサーバーに送り返されることはありません-クライアントとサーバー間のトラフィック量を減らします。
    3. LocalStorageに保存されたデータは、明示的に削除されるまで持続します。行われた変更は保存され、現在および将来のサイトへのすべての訪問で利用可能になります。

    短所

    1. same-Origin policy で動作します。そのため、保存されたデータは同じOriginでのみ使用可能になります。
  2. Cookies

    長所:

    1. 他の人と比べて、何も知らない。

    短所:

    1. 4Kの制限は、名前、値、有効期限などを含むCookie全体に対するものです。ほとんどのブラウザをサポートするには、名前を4000バイト未満に、全体のCookieサイズを4093バイト未満に保ちます。
    2. データは、HTTPリクエスト(HTML、画像、JavaScript、CSSなど)ごとにサーバーに送り返され、クライアントとサーバー間のトラフィック量が増加します。

      通常、以下が許可されます。

      • 300合計Cookie
      • 4096バイトクッキーごと
      • ドメインごとに20 Cookie
      • 81920バイトドメインごと(最大サイズ4096 = 81920バイトのCookieを20個与えます。)
  3. sessionStorage

    長所:

    1. localStorageに似ています。
    2. 変更は、ウィンドウ(またはChromeやFirefoxなどのブラウザーのタブ)ごとにのみ使用できます。行われた変更は保存され、現在のページと、同じウィンドウでのサイトへの今後のアクセスに使用できます。ウィンドウを閉じると、ストレージが削除されます

    短所:

    1. データは、設定されたウィンドウ/タブ内でのみ使用可能です。
    2. データは永続的ではありません。つまり、ウィンドウ/タブが閉じられると失われます。
    3. localStorageと同様に、ttは same-Origin policy で動作します。そのため、保存されたデータは同じOriginでのみ使用可能になります。
  4. PostMessage

    長所:

    1. クロスオリジン 通信を安全に有効にします。
    2. データポイントとして、WebKitの実装(SafariおよびChromeで使用)は現在、制限を強制していません(メモリ不足による制限以外)。

    短所:

    1. 現在のウィンドウからウィンドウを開く必要があり、ウィンドウを開いたままにしておけば通信できます。
    2. セキュリティの問題-postMessageを介して文字列を送信すると、他のJavaScriptによって公開された他のpostMessageイベントを取得できますプラグインなので、必ずtargetOriginと、メッセージリスナーに渡されるデータの健全性チェックを実装してください。
  5. PostMessage + SessionStorageの組み合わせ

    PostMessageを使用して複数のタブ間で通信すると同時に、新しく開いたすべてのタブ/ウィンドウでsessionStorageを使用して、渡されるデータを保持します。タブ/ウィンドウが開いている限り、データは保持されます。そのため、オープナーのタブ/ウィンドウが閉じられたとしても、更新された後でも、開かれたタブ/ウィンドウにはデータ全体が含まれます。

AcrossTabs という名前のJavaScriptライブラリを作成しました。これはpostMessage APIを使用してクロスオリジンタブ/ウィンドウ間で通信し、 sessionStorageは、開いているタブ/ウィンドウIDが存続している限り保持します。

15
softvar

人々が使用を検討すべき別の方法は、共有ワーカーです。私はそれが最先端のコンセプトであることを知っていますが、ローカルストレージよりもはるかに高速で、同じOrigin上にいる限り、親/子ウィンドウ間の関係を必要としない共有ワーカーでリレーを作成できます。

私の答えを参照してください here これについての議論については。

7
datasedai

localStorageに基づいて、同じOriginのタブ/ウィンドウ間で同期/通信する小さなオープンソースコンポーネントがあります(免責事項-私は貢献者の1人です!)。

TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);

TabUtils.OnBroadcastMessage("eventName", function (eventDataString) {
    DoSomething();
});

TabUtils.CallOnce("lockname", function () {
    alert("I run only once across multiple tabs");
});

https://github.com/jitbit/TabUtils

追伸イベントがほぼ同時に発生すると、ほとんどの「ロック/ミューテックス/同期」コンポーネントがWebSocket接続で失敗するため、ここでそれを推奨する自由を取りました

6
Alex

公式の Broadcastchannel と同等に機能するモジュールを作成しましたが、localstorage、indexeddb、unix-socketsに基づいたフォールバックがあります。これにより、WebworkersまたはNodeJSでも常に機能するようになります。 pubkey:BroadcastChannel を参照してください

3
pubkey

ライブラリを作成しました sysend.js 、非常に小さいため、ソースコードを確認できます。ライブラリには外部依存関係はありません。

同じブラウザとドメイン内のタブ/ウィンドウ間の通信に使用できます。ライブラリは、BroadcastChannel(サポートされている場合)またはlocalStorageのストレージイベントを使用します。

APIは非常に簡単です。

sysend.on('foo', function(message) {
    console.log(message);
});
sysend.broadcast('foo', {message: 'Hello'});
sysend.broadcast('foo', "hello");
sysend.broadcast('foo'); // empty notification

ブラウザがBroadcastChannelをサポートする場合、リテラルオブジェクトを送信し、そうでない場合は最初にJSONにシリアル化され、反対側で逆シリアル化されます。

最近のバージョンには、クロスドメイン通信用のプロキシを作成するヘルパーAPIもあります。 (ターゲットドメインに単一のhtmlファイルが必要です)。

デモ です。

NOTE:localStorageを使用して同じ機能を実装する場合、IEに問題があります。ストレージイベントは同じウィンドウに送信され、イベントをトリガーし、他のブラウザーでは他のタブ/ウィンドウに対してのみ呼び出されます。

0
jcubic

私は私のブログでこれに関する記事を書きました: http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage- in-a-web-application

storageManagerを作成したライブラリを使用すると、次のようにしてこれを実現できます。

storageManager.savePermanentData('data', 'key'): //saves permanent data
storageManager.saveSyncedSessionData('data', 'key'); //saves session data to all opened tabs
storageManager.saveSessionData('data', 'key'); //saves session data to current tab only
storageManager.getData('key'); //retrieves data

他のシナリオを処理する他の便利な方法もあります

0
adentum