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 heißt, die Kommunikation zwischen Fenstern/Tabs, iFrames, Web Workern und Service Workern erfolgt. Nachrichten, die in einem bestimmten Kanal gepostet werden, werden an alle Zuhörer dieses Kanals gesendet.

Der Konstruktor von BroadcastChannel hat einen einzelnen Parameter: 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 hat einen bestimmten Zweck. Die Broadcast Channel API wurde für die einfache 1:n-Kommunikation zwischen Skripts mit demselben Ursprung entwickelt.

Einige Anwendungsfälle für Fernsehkanä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, geben Sie es an andere geöffnete Seiten weiter.

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: Weisen Sie einen Service Worker an, 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 nicht mehr eine Referenz auf einen Iframe oder Worker 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 außerdem die plattformübergreifende Kommunikation. Die Broadcast Channel API ist Same-Origin. Da Nachrichten garantiert denselben Ursprung haben, 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 einfach nur 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 die Channel Messaging API Nachrichten an mehrere Listener (1:n) weiterleiten kann. MessageChannel ist für die direkte 1:1-Kommunikation zwischen Skripts vorgesehen. Außerdem ist es aufwendiger, da Sie an jedem Ende Kanäle mit einem Anschluss einrichten müssen.

Funktionserkennung und Browserunterstützung

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

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

Es gibt einige polyfills:

Ich habe sie noch nicht ausprobiert, daher kann dein Kilometerstand abweichen.

Ressourcen