it-swarm.com.de

Kommunikation zwischen Registerkarten oder Fenstern

Ich suchte nach einer Möglichkeit, zwischen mehreren Registerkarten oder Fenstern in einem Browser (in derselben Domäne, nicht in CORS) zu kommunizieren, ohne Spuren zu hinterlassen. Es gab mehrere Lösungen:

  1. Fensterobjekt verwenden
  2. POST-Meldung
  3. Kekse
  4. lokaler Speicher

Die erste ist wahrscheinlich die schlechteste Lösung - Sie müssen ein Fenster aus Ihrem aktuellen Fenster öffnen und können dann nur kommunizieren, solange Sie die Fenster offen halten. Wenn Sie die Seite in einem der Fenster neu laden, haben Sie höchstwahrscheinlich die Kommunikation verloren.

Der zweite Ansatz, der postMessage verwendet, ermöglicht wahrscheinlich die Kommunikation zwischen den verschiedenen Ursprungsorten, weist jedoch das gleiche Problem auf wie der erste Ansatz. Sie müssen ein Fensterobjekt pflegen.

Drittens können Sie mithilfe von Cookies einige Daten im Browser speichern. Dies kann effektiv aussehen, als würde eine Nachricht an alle Fenster derselben Domäne gesendet. Das Problem ist jedoch, dass Sie nie wissen können, ob alle Registerkarten die "Nachricht" bereits gelesen haben oder nicht Aufräumen. Sie müssen eine Art Timeout implementieren, um das Cookie regelmäßig zu lesen. Darüber hinaus sind Sie auf die maximale Cookie-Länge von 4 KB beschränkt.

Die vierte Lösung, die localStorage verwendet, schien die Einschränkungen von Cookies zu überwinden, und es kann sogar vorkommen, dass Ereignisse verwendet werden. Wie man es benutzt, ist in der akzeptierten Antwort beschrieben.

Edit 2018: Die akzeptierte Antwort funktioniert immer noch, aber es gibt eine neuere Lösung für moderne Browser, BroadcastChannel zu verwenden. In der anderen Antwort finden Sie ein einfaches Beispiel, in dem beschrieben wird, wie Nachrichten mithilfe von BroadcastChannel auf einfache Weise zwischen Registerkarten übertragen werden können.

113
Tomas M

Edit 2018: Sie können BroadcastChannel besser für diesen Zweck verwenden. Weitere Antworten finden Sie unten. Wenn Sie dennoch lokaler Speicher für die Kommunikation zwischen Registerkarten verwenden möchten, gehen Sie folgendermaßen vor:

Um benachrichtigt zu werden, wenn eine Registerkarte eine Nachricht an andere Registerkarten sendet, müssen Sie lediglich das 'Speicher'-Ereignis binden. Führen Sie in allen Registerkarten Folgendes aus:

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

Die Funktion message_receive wird jedes Mal aufgerufen, wenn Sie einen Wert von localStorage auf einer anderen Registerkarte festlegen. Der Ereignis-Listener enthält auch die Daten, die neu auf localStorage gesetzt wurden, sodass Sie nicht einmal das localStorage-Objekt selbst analysieren müssen. Dies ist sehr praktisch, da Sie den Wert gleich nach der Einstellung zurücksetzen können, um Spuren effektiv zu entfernen. Hier sind Funktionen für die Nachrichtenübermittlung:

// 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.
}

Sobald Ihre Registerkarten nun an das Onstorage-Ereignis gebunden sind und Sie diese beiden Funktionen implementiert haben, können Sie einfach eine Nachricht an andere Registerkarten senden, die aufrufen, beispielsweise:

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

Denken Sie daran, dass das zweimalige Senden derselben Nachricht nur einmal verbreitet wird. Wenn Sie also Nachrichten wiederholen müssen, fügen Sie ihnen eine eindeutige Kennung hinzu, z

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

Denken Sie auch daran, dass die aktuelle Registerkarte, auf der die Nachricht gesendet wird, diese nicht wirklich empfängt, sondern nur andere Registerkarten oder Fenster in derselben Domäne.

Sie können fragen, was passiert, wenn der Benutzer eine andere Webseite lädt oder seine Registerkarte unmittelbar nach dem Aufruf von setItem () vor dem Element removeItem () schließt. Nun, aus meinem eigenen Test setzt der Browser das Entladen in die Warteschleife, bis die gesamte Funktion message_broadcast() beendet ist. Ich habe getestet, um einen sehr langen Zyklus für () einzugeben, und es wartete immer noch, bis der Zyklus beendet war, bevor er geschlossen wurde. Wenn der Benutzer die Registerkarte nur zwischendurch beendet, hat der Browser nicht genug Zeit, um die Nachricht auf der Festplatte zu speichern. Daher scheint mir dieser Ansatz eine sichere Art zu sein, Nachrichten ohne Spuren zu senden. Kommentare sind willkommen.

110
Tomas M

Für diesen Zweck gibt es eine moderne API - Broadcast Channel

Es ist so einfach wie:

var bc = new BroadcastChannel('test_channel');

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

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

Es ist nicht erforderlich, dass die Nachricht nur ein DOMString ist. Es können beliebige Objekte gesendet werden.

Abgesehen von der API-Sauberkeit ist dies wahrscheinlich der Hauptvorteil dieser API - keine Objekt-Stringifizierung.

Derzeit wird nur in Chrome und Firefox unterstützt , Sie können jedoch eine Polyfill finden, die localStorage verwendet.

71
user

Für diejenigen, die nach einer Lösung suchen, die nicht auf jQuery basiert, ist dies eine einfache JavaScript-Version der von Thomas M bereitgestellten Lösung:

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);
    }
}
29
Nacho Coloma

Checkout AcrossTabs - Einfache Kommunikation zwischen Cross-Origin-Browser-Registerkarten. Es verwendet eine Kombination aus postMessage und sessionStorage API, um die Kommunikation wesentlich einfacher und zuverlässiger zu gestalten.


Es gibt verschiedene Ansätze und jeder hat seine eigenen Vor- und Nachteile. Lass uns jeweils diskutieren:

  1. Lokaler Speicher

    Pros

    1. Der Web-Speicher kann vereinfacht als Verbesserung der Cookies angesehen werden, wodurch die Speicherkapazität erheblich erhöht wird. Wenn Sie sich den Mozilla-Quellcode anschauen, können Sie feststellen, dass 5120KB (5MB, was 2,5 Millionen Zeichen in Chrome entspricht) die Standardspeichergröße für eine gesamte Domäne ist. Dadurch haben Sie wesentlich mehr Platz als mit einem typischen 4-KB-Cookie.
    2. Die Daten werden nicht für jede HTTP-Anforderung (HTML, Bilder, JavaScript, CSS usw.) an den Server zurückgesendet. Dadurch wird der Datenverkehr zwischen Client und Server reduziert.
    3. Die in localStorage gespeicherten Daten bleiben bestehen, bis sie explizit gelöscht werden. Die vorgenommenen Änderungen werden gespeichert und stehen für alle aktuellen und zukünftigen Besuche der Website zur Verfügung.

    Cons:

    1. Es funktioniert auf Same-Origin-Richtlinien . Die gespeicherten Daten sind daher nur auf demselben Ursprung verfügbar.
  2. Kekse

    Pros:

    1. Im Vergleich zu anderen gibt es nichts AFAIK. 

    Nachteile:

    1. Die Obergrenze von 4 KB gilt für das gesamte Cookie, einschließlich Name, Wert, Ablaufdatum usw. Um die meisten Browser zu unterstützen, sollten Sie den Namen unter 4000 Byte und die Gesamt-Cookie-Größe unter 4093 Byte halten.
    2. Die Daten werden für jede HTTP-Anforderung (HTML, Bilder, JavaScript, CSS usw.) an den Server zurückgesendet. Dadurch wird der Datenverkehr zwischen Client und Server erhöht.

      Normalerweise sind folgende zulässig:

      • 300 insgesamt Cookies
      • 4096 Byte pro Cookie
      • 20 Cookies pro Domain
      • 81920 Bytes pro Domain (20 Cookies maximaler Größe 4096 = 81920 Bytes.)
  3. sessionStorage

    Pros:

    1. Es ist ähnlich wie localStorage.
    2. Änderungen sind nur pro Fenster (oder Registerkarten in Browsern wie Chrome und Firefox) verfügbar. Die vorgenommenen Änderungen werden gespeichert und sind für die aktuelle Seite sowie für zukünftige Besuche der Website im selben Fenster verfügbar. Sobald das Fenster geschlossen ist, wird der Speicher gelöscht

    Nachteile:

    1. Die Daten sind nur innerhalb des Fensters/Registers verfügbar, in dem sie festgelegt wurden.
    2. Die Daten sind nicht dauerhaft, d. H. Sie gehen verloren, sobald das Fenster/die Registerkarte geschlossen wird.
    3. Wie localStorage arbeitet tt mit der same-Origin-Richtlinie . Die gespeicherten Daten sind daher nur auf demselben Ursprung verfügbar.
  4. POST-Meldung

    Pros:

    1. Ermöglicht auf sichere Weise Cross-Origin Kommunikation.
    2. Als Datenpunkt erzwingt die WebKit-Implementierung (die von Safari und Chrome verwendet wird) derzeit keine Beschränkungen (außer denen, die durch unzureichenden Arbeitsspeicher auferlegt werden).

    Nachteile:

    1. Müssen Sie ein Fenster aus dem aktuellen Fenster öffnen und können dann nur kommunizieren, solange Sie die Fenster offen halten.
    2. Sicherheitsprobleme - Beim Senden von Strings über postMessage werden andere PostMessage-Ereignisse abgerufen, die von anderen JavaScript-Plugins veröffentlicht wurden. Stellen Sie daher sicher, dass Sie eine targetOrigin und eine Vernunftsprüfung für die Weitergabe der Daten durchführen der Nachrichten-Listener.
  5. Eine Kombination aus PostMessage + SessionStorage

    Verwenden von postMessage für die Kommunikation zwischen mehreren Registerkarten und gleichzeitig zur Verwendung von sessionStorage in allen neu geöffneten Registerkarten/Fenstern, um die übergebenen Daten beizubehalten. Die Daten bleiben erhalten, solange die Tabs/Fenster geöffnet bleiben. Selbst wenn der Opener-Tab/das Opener-Fenster geschlossen wird, haben die geöffneten Tabs/Fenster die gesamten Daten, auch wenn sie aktualisiert wurden.

Ich habe dazu eine JavaScript-Bibliothek mit dem Namen AcrossTabs geschrieben, die postMessage-API für die Kommunikation zwischen Origin-Registerkarten/Windows und sessionStorage verwendet, um die geöffneten Tabs/Windows-Identitäten beizubehalten, solange sie leben.

14
softvar

Eine andere Methode, die Leute in Betracht ziehen sollten, ist Shared Workers. Ich weiß, dass es ein innovatives Konzept ist, aber Sie können ein Relay auf einem Shared Worker erstellen, das VIEL schneller ist als der lokale Speicher und erfordert keine Beziehung zwischen dem übergeordneten/untergeordneten Fenster, solange Sie sich im selben Ursprung befinden.

Siehe meine Antwort hier für einige Diskussionen, die ich dazu gemacht habe.

7
datasedai

Es gibt eine winzige Open-Source-Komponente zum Synchronisieren/Kommunizieren zwischen Registerkarten/Fenstern desselben Ursprungs (Disclaimer - Ich bin einer der Mitwirkenden!), Basierend auf localStorage.

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

P.S. Ich habe mir die Freiheit genommen, es hier zu empfehlen, da die meisten "lock/mutex/sync" -Komponenten auf Websocket-Verbindungen ausfallen, wenn Ereignisse fast gleichzeitig auftreten

6
Alex

Ich habe ein Modul erstellt, das dem offiziellen Broadcastchannel entspricht, aber Fallbacks basierend auf Localstorage, Indexeddb und Unix-Sockets enthält. Dies stellt sicher, dass es auch mit Webworkern oder NodeJS immer funktioniert. Siehe pubkey: BroadcastChannel

2
pubkey

Ich habe eine Bibliothek sysend.js erstellt. Sie ist sehr klein. Sie können den Quellcode überprüfen. Die Bibliothek hat keine externen Abhängigkeiten.

Sie können es für die Kommunikation zwischen Registerkarten/Fenstern in demselben Browser und in derselben Domäne verwenden. Die Bibliothek verwendet BroadcastChannel, falls unterstützt, oder ein Speicherereignis von localStorage.

API ist sehr einfach:

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

wenn Ihr Browser BroadcastChannel unterstützt, hat er ein literales Objekt gesendet, und wenn nicht, wird es zuerst in JSON serialisiert und an anderer Stelle deserialisiert.

Die neueste Version verfügt auch über eine Hilfs-API zum Erstellen eines Proxy für die domänenübergreifende Kommunikation. (Dies erfordert eine einzige HTML-Datei in der Zieldomäne).

Hier ist Demo .

NOTE: Wenn Sie dieselbe Funktionalität mit localStorage implementieren, liegt ein Problem im IE vor. Das Speicherereignis wird an dasselbe Fenster gesendet, das das Ereignis ausgelöst hat, und für andere Browser wird es nur für andere Tabs/Fenster aufgerufen.

0
jcubic

Ich habe dazu in meinem Blog einen Artikel geschrieben: http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality- using-token-based-authentication-and-localstorage-in- a-web-anwendung .

Mit einer Bibliothek, die ich storageManager erstellt habe, können Sie dies wie folgt erreichen:

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

Es gibt andere bequeme Methoden, um auch andere Szenarien zu behandeln

0
adentum