BroadcastChannel API – ein Nachrichtenbus für das Web

Mit der BroadcastChannel API können Skripts mit demselben Ursprung Nachrichten an andere Browserkontexte senden. Man kann ihn sich als einen einfachen Nachrichtenbus vorstellen, der eine Pub/Sub-Semantik zwischen Fenstern/Tabs, iFrames, Web Workern und Service Workern ermöglicht.

API-Grundlagen

Die Broadcast Channel API ist eine einfache API, die die Kommunikation zwischen Suchkontexten erleichtert. Das heißt, die Kommunikation zwischen Fenstern/Tabs, iFrames, Web Workern und Service Workern erfolgt. Nachrichten, die auf einem bestimmten Kanal gepostet werden, werden an alle Hörer dieses Kanals zugestellt.

Der BroadcastChannel-Konstruktor verwendet einen einzelnen Parameter: den Namen eines Kanals. Der Name identifiziert den Kanal und wird in verschiedenen Browserkontexten verwendet.

// 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 beliebige Elemente sein, die vom Algorithmus strukturierter Klon 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 wird nicht an sich selbst übertragen. Wenn Sie also einen onmessage-Listener haben, auf derselben Seite wie ein postMessage() in demselben Kanal, dieses message-Ereignis nicht feuert.

Unterschiede zu anderen Verfahren

An dieser Stelle fragen Sie sich vielleicht, in welchem Zusammenhang dies mit anderen Techniken zur Nachrichtenweitergabe wie WebSockets, SharedWorkers, der MessageChannel API und window.postMessage() steht. Die Broadcast Channel API ersetzt diese APIs nicht. Jedes erfüllt einen bestimmten Zweck. Die Broadcast Channel API wurde für eine einfache 1:n-Kommunikation zwischen Skripts mit demselben Ursprung entwickelt.

Einige Anwendungsfälle für Fernsehkanäle:

  • Nutzeraktionen auf anderen Tabs erkennen
  • Sie werden informiert, wenn sich ein Nutzer in einem anderen Fenster/Tab in einem Konto anmeldet.
  • Weisen Sie einen Mitarbeiter an, einige Aufgaben im Hintergrund auszuführen.
  • Sie werden informiert, wenn 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, auf der sich erkennen lässt, wann sich der Nutzer abmeldet, auch wenn er sich von einem anderen geöffneten Tab auf derselben Website aus abgemeldet hat:

<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>

Nehmen wir auch an, Sie möchten einen Service Worker anweisen, Inhalte im Cache gespeichert, nachdem der Nutzer seine Einstellung für die Offlinespeicherung geändert hat in Ihrer App. Sie können ihre Caches mit window.caches löschen, aber der Service Worker kann enthalten bereits ein Dienstprogramm dafür. Mit der Broadcast Channel API diesen Code wiederverwenden! Ohne die Broadcast Channel API müssten Sie die Ergebnisse von self.clients.matchAll() in einer Schleife wiedergeben und auf jedem Client postMessage() aufrufen, um die Kommunikation von einem Service Worker mit allen seinen Clients zu erreichen (tatsächlicher Code, der dies bewirkt). Wenn du einen Übertragungskanal verwendest, wird dieser O(1) anstelle von O(N) verwendet.

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});
    });
    }
};

Differenz zu postMessage()

Im Gegensatz zu postMessage() müssen Sie keinen Verweis mehr auf einen iFrame oder Worker beibehalten, 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');

Mit window.postMessage() können Sie auch über mehrere Ursprünge hinweg kommunizieren. 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);
};

Einfach abonnieren zu einem bestimmten Kanal und ermöglichen eine sichere, bidirektionale Kommunikation.

Unterschied zu SharedWorkers

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

Für ausgefallenere Anwendungsfälle wie das Verwalten von Sperren, des gemeinsamen Status, das Synchronisieren 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 der BroadcastChannel ist, dass sie Nachrichten an mehrere Listener (1:n) weiterleiten kann. MessageChannel ist für die direkte 1:1-Kommunikation zwischen Skripts vorgesehen. Es ist auch aufwendiger, da Sie Kanäle mit einem Port an beiden Enden 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