API BroadcastChannel – Bus de messages pour le Web

L'API BroadcastChannel permet aux scripts de même origine d'envoyer des messages à d'autres contextes de navigation. Il peut être considéré comme un simple bus de messages qui permet une sémantique pub/sub entre les fenêtres/onglets, les iFrames, les Web Workers et les service workers.

Principes de base des API

L'API Broadcast Channel est une API simple qui facilite la communication entre les contextes de navigation. C'est-à-dire la communication entre les fenêtres/onglets, les iFrames, les Web Workers et les services workers. Les messages publiés sur une chaîne donnée sont distribués à tous les auditeurs de cette chaîne.

Le constructeur BroadcastChannel n'accepte qu'un seul paramètre: le nom d'un canal. Le nom permet d'identifier la chaîne et se situe dans différents contextes de navigation.

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

Envoyer des messages

Les messages peuvent être des chaînes ou tout élément compatible avec l'algorithme de clone structuré (chaînes, objets, tableaux, objets blob, ArrayBuffer, carte).

Exemple : envoi d'un objet Blob ou File

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

Une chaîne ne diffuse aucune annonce vers elle-même. Par conséquent, si vous disposez d'un écouteur onmessage sur la même page qu'un postMessage() sur le même canal, cet événement message ne se déclenche pas.

Différences avec d'autres techniques

À ce stade, vous vous demandez peut-être en quoi cela est lié à d'autres techniques de transmission de messages telles que WebSockets, SharedWorkers, l'API MessageChannel et window.postMessage(). L'API Broadcast Channel ne remplace pas ces API. Chacune sert un objectif. L'API Broadcast Channel est conçue pour faciliter la communication un à plusieurs entre des scripts ayant la même origine.

Voici quelques cas d'utilisation des canaux de diffusion:

  • Détecter les actions des utilisateurs dans d'autres onglets
  • Savoir quand un utilisateur se connecte à un compte dans une autre fenêtre/un autre onglet
  • Demander à un worker d'effectuer une tâche en arrière-plan
  • Savoir quand un service a terminé une action
  • Lorsque l'utilisateur importe une photo dans une fenêtre, transmettez-la sur d'autres pages ouvertes.

Exemple : page qui sait quand l'utilisateur se déconnecte, même depuis un autre onglet ouvert sur le même site :

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

Prenons un autre exemple. Imaginons que vous souhaitiez demander à un service worker de supprimer le contenu mis en cache une fois que l'utilisateur a modifié son "paramètre de stockage hors connexion" dans votre application. Vous pouvez supprimer ses caches à l'aide de window.caches, mais le service worker peut déjà contenir un utilitaire pour ce faire. Nous pouvons utiliser l'API Broadcast Channel pour réutiliser ce code. Sans l'API Broadcast Channel, vous devriez effectuer une boucle sur les résultats de self.clients.matchAll() et appeler postMessage() sur chaque client pour assurer la communication d'un service worker avec tous ses clients (code réel qui fait cela). L'utilisation d'un canal de diffusion génère O(1) au lieu de O(N).

Exemple : demander à un service worker de supprimer un cache en réutilisant ses méthodes utilitaires internes.

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

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

Différence avec postMessage()

Contrairement à postMessage(), vous n'avez plus besoin de conserver une référence à un iframe ou à un nœud de calcul pour communiquer avec eux:

// 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() vous permet également de communiquer entre différentes origines. L'API Broadcast Channel est de même origine. Étant donné que les messages proviennent de la même origine, il n'est pas nécessaire de les valider comme nous le faisions avec 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);
};

Il vous suffit de vous "abonner" à un canal spécifique pour bénéficier d'une communication bidirectionnelle et sécurisée.

Différence avec les SharedWorkers

Utilisez BroadcastChannel dans les cas simples où vous devez envoyer un message à plusieurs fenêtres/onglets ou nœuds de calcul.

Pour les cas d'utilisation plus pointus, tels que la gestion de verrous, l'état partagé, la synchronisation de ressources entre un serveur et plusieurs clients ou le partage d'une connexion WebSocket avec un hôte distant, les nœuds de calcul partagés sont la solution la plus appropriée.

Différence avec l'API MessageChannel

La principale différence entre l'API Channel Messaging et BroadcastChannel est que cette dernière permet d'envoyer des messages à plusieurs écouteurs (un à plusieurs). MessageChannel est destiné à la communication directe entre scripts. Elle est également plus complexe, car vous devez configurer des canaux avec un port à chaque extrémité.

Détection des fonctionnalités et compatibilité avec les navigateurs

Actuellement, Chrome 54, Firefox 38 et Opera 41 sont compatibles avec l'API Broadcast Channel.

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

En ce qui concerne les polyfills, il en existe quelques-uns:

Je ne les ai pas essayés, donc les résultats peuvent varier.

Ressources