BroadcastChannel API — шина сообщений для Интернета.

API BroadcastChannel позволяет сценариям того же источника отправлять сообщения в другие контексты просмотра. Его можно рассматривать как простую шину сообщений, которая обеспечивает семантику публикации/подписки между окнами/вкладками, iframe, веб-работниками и сервис-воркерами.

Основы API

API широковещательного канала — это простой API, который упрощает взаимодействие между контекстами просмотра. То есть взаимодействие между окнами/вкладками, iframe, веб-работниками и сервис-воркерами. Сообщения, отправленные на определенный канал, доставляются всем слушателям этого канала.

Конструктор BroadcastChannel принимает единственный параметр: имя канала. Имя идентифицирует канал и учитывается в контекстах просмотра.

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

Отправка сообщений

Сообщения могут быть строками или чем-то еще, поддерживаемым алгоритмом структурированного клонирования (строки, объекты, массивы, большие двоичные объекты, ArrayBuffer, Map).

Пример — отправка Blob или файла

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

Канал не будет вещать сам на себя. Поэтому, если у вас есть прослушиватель onmessage на той же странице, что и postMessage() на том же канале, это событие message не срабатывает.

Отличия от других техник

На этом этапе вам может быть интересно, как это связано с другими методами передачи сообщений, такими как WebSockets, SharedWorkers, API MessageChannel и window.postMessage() . API широковещательного канала не заменяет эти API. Каждый служит определенной цели. API широковещательного канала предназначен для упрощения связи «один ко многим» между сценариями в одном источнике .

Некоторые варианты использования вещательных каналов:

  • Обнаружение действий пользователя на других вкладках
  • Знайте, когда пользователь входит в учетную запись в другом окне/вкладке.
  • Поручите работнику выполнить некоторую фоновую работу
  • Знайте, когда служба завершает выполнение какого-либо действия.
  • Когда пользователь загружает фотографию в одно окно, передайте ее на другие открытые страницы.

Пример — страница, которая знает, когда пользователь выходит из системы, даже из другой открытой вкладки на том же сайте:

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

В другом примере предположим, что вы хотите поручить сервисному работнику удалить кэшированный контент после того, как пользователь изменит свои «настройки автономного хранилища» в вашем приложении. Вы можете удалить их кеши с помощью window.caches , но сервис-воркер уже может содержать утилиту для этого. Мы можем использовать API широковещательного канала для повторного использования этого кода! Без API широковещательного канала вам пришлось бы перебирать результаты self.clients.matchAll() и вызывать postMessage() на каждом клиенте, чтобы обеспечить связь от сервисного работника со всеми его клиентами ( фактический код, который делает это ). Использование широковещательного канала делает это O(1) вместо O(N) .

Пример — поручить сервисному работнику удалить кеш, повторно используя его внутренние служебные методы.

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

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

Разница с postMessage()

В отличие от postMessage() , вам больше не нужно поддерживать ссылку на iframe или worker для связи с ним:

// 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() также позволяет вам общаться между источниками. API широковещательного канала имеет одно происхождение . Поскольку сообщения гарантированно приходят из одного и того же источника, нет необходимости проверять их, как мы делали это с помощью 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);
};

Просто «подпишитесь» на определенный канал и получите безопасную двустороннюю связь!

Разница с SharedWorkers

Используйте BroadcastChannel для простых случаев, когда вам нужно отправить сообщение потенциально нескольким окнам/вкладкам или работникам.

Для более сложных случаев использования, таких как управление блокировками, общее состояние, синхронизация ресурсов между сервером и несколькими клиентами или совместное использование соединения WebSocket с удаленным хостом, наиболее подходящим решением являются общие рабочие процессы.

Разница с API MessageChannel

Основное различие между API Channel Messaging и BroadcastChannel заключается в том, что последний является средством отправки сообщений множеству прослушивателей (один ко многим). MessageChannel предназначен для индивидуальной связи напрямую между скриптами. Это также более сложный процесс, требующий настройки каналов с портом на каждом конце.

Обнаружение функций и поддержка браузера

В настоящее время Chrome 54, Firefox 38 и Opera 41 поддерживают API широковещательного канала.

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

Что касается полифилов, то их несколько:

Я их не пробовал, поэтому ваши впечатления могут отличаться.

Ресурсы