API BroadcastChannel - Un bus di messaggi per il Web

L'API BroadcastChannel consente agli script della stessa origine di inviare messaggi ad altri contesti di navigazione. Può essere pensato come un semplice bus di messaggi che consente la semantica Pub/Sub tra finestre/schede, iframe, web worker e service worker.

Nozioni di base sulle API

L'API Broadcast Channel è un'API semplice che semplifica la comunicazione tra i contesti di navigazione. cioè comunicare tra finestre/schede, iframe, web worker e service worker. I messaggi pubblicati su un determinato canale vengono inviati a tutti gli ascoltatori di quel canale.

Il costruttore BroadcastChannel utilizza un singolo parametro: il nome di un canale. Il nome identifica il canale e si adatta ai vari contesti di navigazione.

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

l'invio di un messaggio e un altro

I messaggi possono essere stringhe o qualsiasi altro supporto supportato dall'algoritmo del clone strutturato (stringhe, oggetti, array, BLOB, array buffer, mappa).

Esempio - invio di un BLOB o file

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

Un canale non viene trasmesso a se stesso. Pertanto, se hai un listener onmessage sulla stessa pagina di un postMessage() per lo stesso canale, l'evento message non si accende.

Differenze con altre tecniche

A questo punto ti starai chiedendo in che modo ciò riguarda altre tecniche per la trasmissione dei messaggi come WebSocket, SharedWorkers, l'API MessageChannel e window.postMessage(). L'API Broadcast Channel non sostituisce queste API. Ognuna ha uno scopo. L'API Broadcast Channel è progettata per facilitare la comunicazione one-to-many tra gli script sulla stessa origine.

Alcuni casi d'uso per i canali televisivi:

  • Rileva le azioni degli utenti in altre schede
  • Scopri quando un utente accede a un account in un'altra finestra/scheda.
  • Chiedi a un worker di eseguire alcuni lavori in background
  • Scoprire quando un servizio esegue un'azione.
  • Quando l'utente carica una foto in una finestra, falla passare ad altre pagine aperte.

Esempio: pagina che capisce quando l'utente si disconnette, anche da un'altra scheda aperta sullo stesso sito:

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

In un altro esempio, supponiamo che tu voglia chiedere a un service worker di rimuovere contenuti memorizzati nella cache dopo che l'utente ha modificato la propria "impostazione di archiviazione offline" all'interno dell'app. Potresti eliminare le cache utilizzando window.caches, ma il service worker potrebbe contengono già un'utilità per farlo. Possiamo usare l'API Broadcast Channel per riutilizzalo! Senza l'API Broadcast Channel, dovresti eseguire il loop sui risultati di self.clients.matchAll() e chiamare postMessage() su ciascun client per ottenere la comunicazione da un service worker a tutti i suoi client (codice effettivo che esegue questa operazione). L'utilizzo di un canale di trasmissione genera questo O(1) anziché O(N).

Esempio: indica a un service worker di rimuovere una cache, riutilizzando i suoi metodi di utilità interna.

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

Differenza con postMessage()

A differenza di postMessage(), non è più necessario gestire un riferimento a un iframe o a un worker per poter comunicare con quell'iframe:

// 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() ti consente anche di comunicare tra origini. L'API Broadcast Channel ha la stessa origine. Poiché è garantito che i messaggi provengano dalla stessa origine, non è necessario convalidarli come facevamo prima con 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);
};

Basta "Iscriviti" a un determinato canale e garantire una comunicazione sicura e bidirezionale.

Differenza con SharedWorkers

Utilizza BroadcastChannel per i casi semplici in cui devi inviare un messaggio a diverse finestre/schede o a diversi worker.

Per casi d'uso più elaborati, come la gestione dei blocchi, dello stato condiviso, la sincronizzazione delle risorse tra un server e più client o la condivisione di una connessione WebSocket con un host remoto, i worker condivisi sono la soluzione più appropriata.

Differenza con l'API MessageChannel

La differenza principale tra l'API Channel Messaging e BroadcastChannel è che quest'ultima è un mezzo per inviare messaggi a più listener (one-to-many). MessageChannel è pensato per la comunicazione one-to-one diretta tra gli script. Inoltre, richiede di configurare canali con una porta su entrambe le estremità.

Rilevamento delle funzionalità e supporto del browser

Attualmente, Chrome 54, Firefox 38 e Opera 41 supportano l'API Broadcast Channel.

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

Per quanto riguarda i polyfill, ce ne sono alcuni:

Non ho provato questi suggerimenti, quindi il chilometraggio potrebbe variare.

Risorse