BroadcastChannel API – magistrala komunikatów dla internetu

Interfejs BroadcastChannel API umożliwia skryptom tej samej domeny wysyłanie wiadomości do innych kontekstów przeglądania. Można ją traktować jako prostą magistralę wiadomości, która umożliwia semantykę pub/Sub między oknami/kartami, elementami iframe, instancjami internetowymi oraz mechanizmami Service Worker.

Podstawowe informacje o interfejsie API

Broadcast Channel API to prosty interfejs API, który ułatwia komunikację między kontekstami przeglądania. Oznacza to komunikację między oknami/kartami, elementami iframe, instancjami internetowymi oraz mechanizmami Service Worker. Wiadomości publikowane na danym kanale trafiają do wszystkich jego słuchaczy.

Konstruktor BroadcastChannel przyjmuje pojedynczy parametr: nazwę kanału. Nazwa identyfikuje kanał i odnosi się do wszystkich kontekstów przeglądania.

// 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();

Wysyłanie wiadomości

Wiadomości mogą być ciągami tekstowymi lub dowolnymi elementami obsługiwanymi przez algorytm klonu strukturalnego (ciągi znaków, obiekty, tablice, obiekty blob, tablicaBuffer czy mapa).

Przykład – wysyłanie obiektu blob lub pliku

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

Kanał nie będzie transmitować do siebie samego. Jeśli masz słuchacz onmessage na tej samej stronie co postMessage() do tego samego kanału, to zdarzenie message nie włącza się.

Różnice w porównaniu z innymi technikami

Być może zastanawiasz się, jak wiąże się to z innymi technikami przekazywania wiadomości, takimi jak WebSockets, SharedWorkers, MessageChannel API i window.postMessage(). Interfejs Broadcast Channel API nie zastępuje tych interfejsów. Każdy z nich ma jakiś cel. Interfejs Broadcast Channel API służy do łatwej komunikacji jeden do wielu między skryptami w tym samym źródle.

Oto kilka przypadków użycia kanałów naziemnych:

  • Wykrywanie działań użytkowników na innych kartach
  • Otrzymuj powiadomienia, gdy użytkownik loguje się na konto w innym oknie lub na innej karcie.
  • Instruowanie pracownika, aby wykonał pracę w tle
  • Wiedz, kiedy usługa wykonuje działanie.
  • Gdy użytkownik przesyła zdjęcie w jednym oknie, przekaż je na inne otwarte strony.

Przykład – strona, która wie, kiedy użytkownik się wylogowuje, nawet z innej otwartej karty w tej samej witrynie:

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

W innym przykładzie załóżmy, że chcesz poprosić skrypt service worker o usunięcie zawartość pamięci podręcznej po zmianie „ustawień przechowywania offline” przez użytkownika w aplikacji. Możesz usunąć ich pamięci podręczne za pomocą window.caches, ale skrypt service worker może zawiera już narzędzie do wykonywania tej czynności. Możemy użyć interfejsu Broadcast Channel API do użyj ponownie tego kodu. Bez interfejsu Broadcast Channel API należałoby zapętlić wyniki funkcji self.clients.matchAll() i wywołać postMessage() w przypadku każdego klienta, aby zapewnić komunikację między mechanizmem Service Worker a wszystkimi klientami (rzeczywisty kod, który to umożliwia). Jeśli używasz kanału transmisji, to O(1) zamiast O(N).

Przykład: poproś skrypt service worker o usunięcie pamięci podręcznej z zastosowaniem wewnętrznych metod narzędzia.

W 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]);

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

Różnica w stosunku do postMessage()

W przeciwieństwie do postMessage() nie musisz już utrzymywać odniesienia do elementu iframe ani instancji roboczej, aby się z nim komunikować:

// 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() umożliwia też komunikację między źródłami. Interfejs Broadcast Channel API pochodzi z tej samej domeny. Wiadomości gwarantują, że będą pochodzić z tego samego źródła, więc nie trzeba weryfikować ich tak jak to było wcześniej w usłudze window.postMessage():

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

Po prostu „zasubskrybuj” z konkretnym kanałem i zapewnić bezpieczną, dwukierunkową komunikację.

Różnica względem obiektów SharedWorkers

BroadcastChannel używaj w prostych przypadkach, gdy musisz wysłać wiadomość do potencjalnie kilku okien/kart lub do instancji roboczych.

W bardziej złożonych przypadkach, takich jak zarządzanie blokadami, udostępnianie stanu, synchronizowanie zasobów między serwerem a wieloma klientami czy udostępnianie połączenia WebSocket z hostem zdalnym, współużytkowane procesy robocze są najlepszym rozwiązaniem.

Różnica w stosunku do interfejsu MessageChannel API

Główna różnica między interfejsem Channel Messaging API a BroadcastChannel polega na tym, że ten drugi sposób służy do wysyłania wiadomości do wielu detektorów (jeden do wielu). MessageChannel służy do komunikacji jeden do jednego bezpośrednio między skryptami. Jest to również bardziej pracochłonne, ponieważ musisz skonfigurować kanały z portem z obu stron.

Wykrywanie funkcji i obsługa przeglądarki

Obecnie interfejs Broadcast Channel API obsługują przeglądarki Chrome 54, Firefox 38 i Opera 41.

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

Jest ich kilka:

Nie wypróbowałem(am) tych opcji, więc Twój dystans może być różny.

Zasoby