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!
}
Что касается полифилов, то их несколько:
- https://gist.github.com/alexis89x/041a8e20a9193f3c47fb
- https://gist.github.com/inexorabletash/52f437d1451d12145264
Я их не пробовал, поэтому ваши впечатления могут отличаться.