BroadcastChannel API – ein Nachrichtenbus für das Web

Mit der BroadcastChannel API können Scripts desselben Ursprungs Nachrichten an andere Browserkontexte senden. Es kann als einfacher Nachrichtenbus betrachtet werden, der Pub/Sub-Semantik zwischen Fenstern/Tabs, Iframes, Webworkern und Serviceworkern ermöglicht.

API-Grundlagen

Die Broadcast Channel API ist eine einfache API, die die Kommunikation zwischen Browserkontexten erleichtert. Das bedeutet, dass die Kommunikation zwischen Fenstern/Tabs, Iframes, Webworkern und Serviceworkern erfolgt. Nachrichten, die in einem bestimmten Kanal gepostet werden, werden an alle Zuhörer dieses Kanals gesendet.

Der Konstruktor von BroadcastChannel nimmt einen einzelnen Parameter an: den Namen eines Kanals. Der Name identifiziert den Kanal und ist unabhängig vom Browserkontext.

// Connect to the channel named "my_bus".
const channel = new BroadcastChannel('my_bus');

// Send a message on "my_bus".
channel.postMessage('This is a test message.');

// Listen for messages on "my_bus".
channel.onmessage = function(e) {
    console.log('Received', e.data);
};

// Close the channel when you're done.
channel.close();

Nachrichten senden

Nachrichten können Strings oder andere Elemente sein, die vom Algorithmus für strukturierte Klone unterstützt werden (Strings, Objekte, Arrays, Blobs, ArrayBuffer, Map).

Beispiel: Senden eines Blobs oder einer Datei

channel.postMessage(new Blob(['foo', 'bar'], {type: 'plain/text'}));

Ein Kanal kann nicht auf sich selbst senden. Wenn Sie also einen onmessage-Listener auf derselben Seite wie einen postMessage() für denselben Kanal haben, wird das message-Ereignis nicht ausgelöst.

Unterschiede zu anderen Techniken

An dieser Stelle fragen Sie sich vielleicht, wie sich das mit anderen Techniken zur Nachrichtenübertragung wie WebSockets, SharedWorkers, der MessageChannel API und window.postMessage() verhält. Die Broadcast Channel API ersetzt diese APIs nicht. Jede dient einem bestimmten Zweck. Die Broadcast Channel API ist für die einfache Kommunikation zwischen Scripts mit demselben Ursprung vorgesehen.

Anwendungsfälle für Broadcast-Kanäle:

  • Nutzeraktionen auf anderen Tabs erkennen
  • Sie können erkennen, wenn sich ein Nutzer in einem anderen Fenster oder Tab in einem Konto anmeldet.
  • Einen Worker anweisen, Hintergrundarbeit auszuführen
  • Sie erfahren, wann ein Dienst eine bestimmte Aktion ausgeführt hat.
  • Wenn der Nutzer ein Foto in einem Fenster hochlädt, kann er es an andere geöffnete Seiten weitergeben.

Beispiel: Seite, die weiß, wann sich der Nutzer abmeldet, auch wenn er sich auf einem anderen geöffneten Tab auf derselben Website befindet:

<button id="logout">Logout</button>

<script>
function doLogout() {
    // update the UI login state for this page.
}

const authChannel = new BroadcastChannel('auth');

const button = document.querySelector('#logout');
button.addEventListener('click', e => {
    // A channel won't broadcast to itself so we invoke doLogout()
    // manually on this page.
    doLogout();
    authChannel.postMessage({cmd: 'logout', user: 'Eric Bidelman'});
});

authChannel.onmessage = function(e) {
    if (e.data.cmd === 'logout') {
    doLogout();
    }
};
</script>

Angenommen, Sie möchten einen Service Worker anweisen, zwischengespeicherte Inhalte zu entfernen, nachdem der Nutzer die Einstellung „Offlinespeicher“ in Ihrer App geändert hat. Sie könnten die Caches mit window.caches löschen, aber der Service Worker enthält möglicherweise bereits ein Dienstprogramm, das dies erledigt. Mit der Broadcast Channel API können wir diesen Code wiederverwenden. Ohne die Broadcast Channel API müssten Sie die Ergebnisse von self.clients.matchAll() in einer Schleife durchgehen und postMessage() auf jedem Client aufrufen, um die Kommunikation zwischen einem Service Worker und allen seinen Clients zu ermöglichen (aktueller Code, der dies tut). Bei Verwendung eines Broadcast-Channels ist das O(1) anstelle von O(N).

Beispiel: Einen Service Worker anweisen, einen Cache zu entfernen und dabei seine internen Dienstprogrammmethoden wiederzuverwenden.

In index.html

const channel = new BroadcastChannel('app-channel');
channel.onmessage = function(e) {
    if (e.data.action === 'clearcache') {
    console.log('Cache removed:', e.data.removed);
    }
};

const messageChannel = new MessageChannel();

// Send the service worker a message to clear the cache.
// We can't use a BroadcastChannel for this because the
// service worker may need to be woken up. MessageChannels do that.
navigator.serviceWorker.controller.postMessage({
    action: 'clearcache',
    cacheName: 'v1-cache'
}, [messageChannel.port2]);

In sw.js

function nukeCache(cacheName) {
    return caches.delete(cacheName).then(removed => {
    // ...do more stuff (internal) to this service worker...
    return removed;
    });
}

self.onmessage = function(e) {
    const action = e.data.action;
    const cacheName = e.data.cacheName;

    if (action === 'clearcache') {
    nukeCache(cacheName).then(removed => {
        // Send the main page a response via the BroadcastChannel API.
        // We could also use e.ports[0].postMessage(), but the benefit
        // of responding with the BroadcastChannel API is that other
        // subscribers may be listening.
        const channel = new BroadcastChannel('app-channel');
        channel.postMessage({action, removed});
    });
    }
};

Unterschied zu postMessage()

Anders als bei postMessage() müssen Sie keinen Verweis auf einen Iframe oder Worker mehr pflegen, um mit ihm zu kommunizieren:

// Don't have to save references to window objects.
const popup = window.open('https://another-origin.com', ...);
popup.postMessage('Sup popup!', 'https://another-origin.com');

window.postMessage() ermöglicht auch die plattformübergreifende Kommunikation. Die Broadcast Channel API ist Same-Origin. Da Nachrichten garantiert von derselben Quelle stammen, müssen sie nicht wie bei window.postMessage() validiert werden:

// Don't have to validate the origin of a message.
const iframe = document.querySelector('iframe');
iframe.contentWindow.onmessage = function(e) {
    if (e.origin !== 'https://expected-origin.com') {
    return;
    }
    e.source.postMessage('Ack!', e.origin);
};

Sie müssen lediglich einen bestimmten Kanal abonnieren, um eine sichere, bidirektionale Kommunikation zu ermöglichen.

Unterschied zu SharedWorkers

Verwenden Sie BroadcastChannel für einfache Fälle, in denen Sie eine Nachricht an mehrere Fenster/Tabs oder Worker senden müssen.

Für anspruchsvollere Anwendungsfälle wie das Verwalten von Sperren, den gemeinsamen Zustand, die Synchronisierung von Ressourcen zwischen einem Server und mehreren Clients oder die Freigabe einer WebSocket-Verbindung mit einem Remote-Host sind freigegebene Worker die beste Lösung.

Unterschied zur MessageChannel API

Der Hauptunterschied zwischen der Channel Messaging API und BroadcastChannel besteht darin, dass letztere eine Möglichkeit bietet, Nachrichten an mehrere Listener zu senden (Eins-zu-Viele). MessageChannel ist für die direkte Kommunikation zwischen Scripts gedacht. Außerdem ist es aufwendiger, da Sie an jedem Ende Kanäle mit einem Anschluss einrichten müssen.

Funktionserkennung und Browserunterstützung

Derzeit wird die Broadcast Channel API von Chrome 54, Firefox 38 und Opera 41 unterstützt.

if ('BroadcastChannel' in self) {
    // BroadcastChannel API supported!
}

Es gibt einige polyfills:

Ich habe diese Optionen nicht selbst ausprobiert, daher kann die Leistung variieren.

Ressourcen