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:
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.
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.
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.
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);
}
}
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:
Pros:
Cons:
Pros:
Nachteile:
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:
Pros:
localStorage
.Nachteile:
localStorage
arbeitet tt mit der same-Origin-Richtlinie . Die gespeicherten Daten sind daher nur auf demselben Ursprung verfügbar.Pros:
Nachteile:
targetOrigin
und eine Vernunftsprüfung für die Weitergabe der Daten durchführen der Nachrichten-Listener.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.
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.
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
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
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.
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