BroadcastChannel API – magistrala komunikatów dla internetu

Interfejs BroadcastChannel API umożliwia skryptom z tego samego źródła wysyłanie wiadomości do innych kontekstów przeglądania. Można go traktować jako prosty interfejs wiadomości, który umożliwia semantykę pub/sub między oknami/kartami, iframe’ami, workerami internetowymi i workerami usługowymi.

Podstawowe informacje o interfejsie API

Broadcast Channel API to prosty interfejs API, który ułatwia komunikację między kontekstami przeglądania. Oznacza to, że skrypty mogą się komunikować z oknami i kartami, elementami iframe, skryptami web worker i skryptami service worker. Wiadomości publikowane na danym kanale są dostarczane do wszystkich słuchaczy tego kanału.

Konstruktor BroadcastChannel przyjmuje pojedynczy parametr: nazwę kanału. Nazwa identyfikuje kanał i jest widoczna w różnych kontekstach 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 znaków lub dowolnymi obiektami obsługiwanymi przez algorytm strukturalnego sklonowania (ciągi znaków, obiekty, tablice, bloby, tablice ArrayBuffer, mapy).

Przykład – wysyłanie bloba lub pliku

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

kanał nie będzie nadawał do siebie samego. Jeśli więc masz detektor onmessage na tej samej stronie co postMessage() dla tego samego kanału, zdarzenie message nie uruchomi się.

Różnice w stosunku do innych technik

Zastanawiasz się pewnie, jak to się ma do innych technik przekazywania wiadomości, takich jak WebSockets, SharedWorkers, interfejs MessageChannel API czy window.postMessage(). Interfejs Broadcast Channel API nie zastępuje tych interfejsów API. Każdy z nich służy do czegoś innego. Interfejs Broadcast Channel API służy do łatwej komunikacji jeden-do-wielu między skryptami w ramach tej samej domeny.

Przykładowe zastosowania kanałów transmisji:

  • Wykrywanie działań użytkowników na innych kartach
  • Dowiedz się, kiedy użytkownik loguje się na konto w innym oknie lub na innej karcie.
  • Instruowanie pracownika, aby wykonał pracę w tle
  • Dowiedz się, kiedy usługa wykonała określone działanie.
  • Gdy użytkownik prześle zdjęcie w jednym oknie, prześlij je na inne otwarte strony.

Przykład: strona, która wie, kiedy użytkownik się wyloguje, 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>

Załóżmy na przykład, że chcesz zlecić pracownikowi usługi usunięcie z pamięci podręcznej treści po tym, jak użytkownik zmieni „ustawienie pamięci offline” w Twojej aplikacji. Możesz usunąć pamięć podręczną za pomocą funkcji window.caches, ale pracownik usługi może już mieć do tego odpowiednie narzędzie. Możemy użyć interfejsu Broadcast Channel API, aby ponownie użyć tego kodu. Bez interfejsu Broadcast Channel API musisz przetworzyć wyniki funkcji self.clients.matchAll() i wywołać funkcję postMessage() w przypadku każdego klienta, aby umożliwić komunikację między usługą wątekową a wszystkimi jej klientami (dodatkowy 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 pliku 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 przypadku postMessage()

W przeciwieństwie do postMessage() nie musisz już utrzymywać odwołania do elementu iframe ani workera, aby się z nimi 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 jest interfejsem typu same-origin. Ponieważ wiadomość pochodzi z tego samego źródła, nie trzeba jej weryfikować tak jak w przypadku 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” konkretny kanał i korzystaj z bezpiecznej, dwukierunkowej komunikacji.

Różnica w przypadku SharedWorkers

Użyj BroadcastChannel w prostych przypadkach, gdy chcesz wysłać wiadomość do kilku okien lub kart lub do pracowników.

W przypadku bardziej zaawansowanych zastosowań, takich jak zarządzanie blokadami, współdzielone stany, synchronizowanie zasobów między serwerem a wielu klientami czy udostępnianie połączenia WebSocket z hostem zdalnym, najbardziej odpowiednie są współdzielone procesy robocze.

Różnica między interfejsem 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 bezpośredniej 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ądarek

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

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

Jest ich kilka:

Nie testowaliśmy tych rozwiązań, więc wyniki mogą się różnić.

Zasoby