Von Zeit zu Zeit müssen Entwickler die Kommunikation zwischen mehreren Browser-Registerkarten herstellen, um Nachrichten von einer Registerkarte zur anderen senden und Antworten empfangen zu können. Wir haben uns auch irgendwann diesem Bedürfnis gestellt.
Einige Lösungen existieren bereits (wie zum Beispiel die BroadcastChannel-API). Die Browserunterstützung lässt jedoch zu wünschen übrig , weshalb wir uns für eine eigene Bibliothek entschieden haben. Als die Bibliothek fertig war, war diese Funktionalität nicht mehr erforderlich. Dennoch ergab sich eine andere Aufgabe: die Kommunikation zwischen einem Iframe und dem Hauptfenster.
Bei näherer Betrachtung stellte sich heraus, dass zwei Drittel der Bibliothek nicht geändert werden mussten - nur einige Code-Umgestaltungen waren erforderlich. Die Bibliothek ist ein Kommunikationsprotokoll, das mit Textdaten arbeiten kann. Es kann in allen Fällen angewendet werden, in denen Text übertragen wird, z. B. iframes, window.open, worker, Browser-Registerkarten oder WebSocket.
Wie es funktioniert
Derzeit hat das Protokoll zwei Funktionen: Senden von Nachrichten und Abonnieren von Ereignissen. Jede Nachricht im Protokoll ist ein Datenobjekt. Für uns ist das Hauptfeld in diesem Objekt der Typ , der uns sagt, um welche Art von Nachricht es sich handelt. Das Typfeld ist eine Aufzählung mit den folgenden Werten:
- 0 - Nachricht senden
- 1 - Senden einer Anfrage
- 2 - eine Antwort erhalten.
Senden einer Nachricht
Das Senden einer Nachricht bedeutet keine Antwort. Um eine Nachricht zu senden, erstellen wir ein Objekt mit den folgenden Feldern:
- Typ - Ereignistyp 0
- name - Name des Benutzerereignisses
- Daten - Benutzerdaten (JSON-ähnlich).
Beim Empfang einer Nachricht auf der anderen Seite mit dem Typfeld = 0 wissen wir, dass es sich um ein Ereignis mit einem vorhandenen Ereignisnamen und Daten handelt. Alles was wir tun müssen, ist das Ereignis zu übertragen (fast ein Standard- EventEmitter- Muster).
So funktioniert es in einem einfachen Schema:

Senden einer Anfrage
Das Senden einer Anfrage impliziert, dass innerhalb der Bibliothek eine Anforderungs- ID erstellt wird und die Bibliothek auf eine Antwort mit der ID wartet. Nach erfolgreichem Empfang einer Antwort werden alle Zusatzfelder entfernt und die Antwort an den Benutzer zurückgegeben. Außerdem kann die maximale Antwortzeit eingestellt werden.

Bei Anfragen ist dies etwas komplizierter. Um auf eine Anfrage zu antworten, müssen Sie die in unserem Protokoll verfügbaren Methoden bekannt geben. Dies erfolgt mit der Methode registerRequestHandler . Es akzeptiert den Namen einer Anforderung für eine Antwort und eine Funktion, die die Antwort zurückgibt. Um eine Anfrage zu erstellen, benötigen wir eine ID , und wir können grundsätzlich einen Zeitstempel verwenden , aber die Anpassung ist nicht sehr bequem. Dies ist also eine Klassen-ID, die eine Antwort + Antwortnummer + Zeichenfolgenliteral sendet. Jetzt erstellen wir ein Objekt mit den folgenden Feldern: ID , Typ = 1 , Name als Anforderungsname und Daten als Benutzerdaten (JSON-ähnlich).
Beim Empfang einer Anfrage prüfen wir, ob wir eine API zur Beantwortung dieser Anfrage haben. Wenn dies nicht der Fall ist, geben wir eine Fehlermeldung zurück. Wenn wir eine API haben, geben wir das Ergebnis der Ausführung der Funktion von registerRequestHandler mit dem entsprechenden Anforderungsnamen zurück.
Für die Antwort erstellen wir ein Objekt mit den Feldern: Typ = 2, ID als ID der Nachricht, auf die wir antworten, Status als Feld, das angibt, ob diese Antwort ein Fehler ist (wenn wir keine API haben, oder der Handler ist ein Fehler aufgetreten, oder der Benutzer hat ein abgelehntes Versprechen zurückgegeben, oder es tritt ein anderer Fehler auf (serialisieren)) und der Inhalt als Antwortdaten.
Daher haben wir die Funktionsweise des Protokolls beschrieben, das die Busklasse ausführt, aber den Prozess des Sendens und Empfangens von Nachrichten nicht erläutert. Dafür benötigen wir Klassenadapter mit drei Methoden.
- send ist eine Methode, die grundsätzlich für das Senden von Nachrichten verantwortlich ist
- addListener ist eine Methode zum Abonnieren von Ereignissen
- destroy ist eine Methode zum Löschen von Abonnements beim Löschen von Bus .
Adapter. Ausführung des Protokolls
Zum Starten des Protokolls ist derzeit nur der Adapter für die Arbeit mit iframe / window bereit. Es verwendet postMessage und addEventListener . Es ist ziemlich einfach: Sie müssen eine Nachricht mit einem korrekten Ursprung an postMessage senden und Nachrichten über addEventListener beim Ereignis "message" abhören.
Wir sind auf einige Nuancen gestoßen:
- Sie sollten die Antworten in IHREM Fenster immer anhören und im ANDEREN Fenster senden (Iframe, Opener, Eltern, Arbeiter usw.). Wenn Sie versuchen, eine Nachricht im ANDEREN Fenster abzuhören und der Ursprung vom aktuellen abweicht, tritt ein Fehler auf.
- Stellen Sie beim Empfang einer Nachricht sicher, dass sie an Sie gerichtet wurde: Das Fenster kann viele Analysemeldungen, WebStorm (falls Sie es verwenden) und andere Iframes aufnehmen. Sie müssen also sicherstellen, dass das Ereignis in Ihrem Protokoll enthalten und für Sie bestimmt ist.
- Sie können ein Versprechen nicht mit einer Windows- Kopie zurückgeben, da das Versprechen bei der Rückgabe des Ergebnisses versucht, zu überprüfen, ob das Ergebnis die then- Methode hat. Wenn Sie keinen Zugriff auf das Fenster haben (z. B. ein Fenster mit einem anderen Ursprung), tritt ein Fehler auf (obwohl dies nicht in allen Browsern der Fall ist). Um dieses Problem zu vermeiden, würde es ausreichen, das Fenster in das Objekt einzuschließen und ein Objekt in das Versprechen aufzunehmen, das einen Link zum richtigen Fenster enthält.
Anwendungsbeispiele:
Die Bibliothek ist in NPM verfügbar und kann einfach über Ihren Paketmanager installiert werden - @ Wave / Waves-Browser-Bus
Um eine bidirektionale Kommunikation mit einem Iframe herzustellen, reicht es aus, diesen Code zu verwenden:
import { Bus, WindowAdapter } from '@waves/waves-browser-bus'; const url = 'https://some-iframe-content-url.com'; const iframe = document.createElement('iframe'); WindowAdapter.createSimpleWindowAdapter(iframe).then(adapter => { const bus = new Bus(adapter); bus.once('ready', () => {
Innerhalb des Iframes:
import { Bus, WindowAdapter } from '@waves/waves-browser-bus'; WindowAdapter.createSimpleWindowAdapter().then(adapter => { const bus = new Bus(adapter); bus.dispatchEvent('ready', null);
Was kommt als nächstes?
Wir haben ein flexibles und vielseitiges Protokoll, das in jeder Situation verwendet werden kann. Als nächstes plane ich, die Adapter vom Protokoll zu trennen und sie in separate npm-Pakete zu packen und Adapter für Worker- und Browser-Registerkarten hinzuzufügen. Ich möchte, dass das Schreiben von Adaptern, die das Protokoll für andere Zwecke ausführen, so einfach wie möglich ist. Wenn Sie am Entwicklungsprozess teilnehmen möchten oder Ideen zur Funktionalität der Bibliothek haben, können Sie sich gerne im Repo an uns wenden .