BroadcastChannel API
一、BroadcastChannel 概述
1.1 什么是 BroadcastChannel
BroadcastChannel API 允许同源(same-origin)的浏览器上下文(标签页、窗口、iframe、worker)之间进行双向通信。与 postMessage 不同,BroadcastChannel 是发布-订阅模式,发送者不需要知道接收者。
// 创建频道
const channel = new BroadcastChannel('settings');
// 发送消息
channel.postMessage({ type: 'theme', payload: 'dark' });
// 接收消息
channel.onmessage = (event) => {
console.log('Received:', event.data);
};
1.2 与其他通信方式对比
| API | 通信范围 | 模式 | 复杂度 |
|---|---|---|---|
| postMessage | 跨窗口/iframe | 点对点 | 高(需要目标引用) |
| BroadcastChannel | 同源广播 | 发布-订阅 | 低 |
| MessageChannel | 双向通道 | 管道 | 中 |
| localStorage 事件 | 同源标签页 | 广播 | 低(仅字符串) |
二、核心 API
2.1 创建频道
const channel = new BroadcastChannel('my-channel');
2.2 发送消息
// 发送任何结构化数据
channel.postMessage({
type: 'update',
data: { id: 1, value: 'test' }
});
// 发送简单消息
channel.postMessage('Hello from channel');
2.3 接收消息
channel.onmessage = (event) => {
const { type, data } = event.data;
console.log('Message received:', type, data);
};
// 也可以使用 addEventListener
channel.addEventListener('message', (event) => {
console.log(event.data);
});
2.4 关闭频道
channel.close();
三、实际应用
3.1 主题同步
// 页面 1(或其他标签页)
const themeChannel = new BroadcastChannel('theme');
function setTheme(theme) {
document.documentElement.dataset.theme = theme;
localStorage.setItem('theme', theme);
themeChannel.postMessage({ theme });
}
// 监听来自其他标签页的主题变化
themeChannel.onmessage = (e) => {
if (e.data.type === 'theme') {
setTheme(e.data.theme);
}
};
3.2 登录状态同步
// 监听登录状态变化
const authChannel = new BroadcastChannel('auth');
authChannel.onmessage = (e) => {
if (e.data.type === 'logout') {
// 清除本地状态并重定向
localStorage.removeItem('token');
window.location.href = '/login';
}
});
// 退出登录时通知其他标签页
function logout() {
authChannel.postMessage({ type: 'logout' });
window.location.href = '/login';
}
3.3 购物车同步
const cartChannel = new BroadcastChannel('cart');
// 添加商品
function addToCart(product) {
cart.push(product);
localStorage.setItem('cart', JSON.stringify(cart));
cartChannel.postMessage({
type: 'cart-update',
cart: cart
});
}
// 监听其他标签页的更新
cartChannel.onmessage = (e) => {
if (e.data.type === 'cart-update') {
updateCartUI(e.data.cart);
}
};
四、Worker 中的使用
4.1 Service Worker 广播
// Service Worker
self.addEventListener('message', (event) => {
if (event.data.type === 'broadcast') {
const channel = new BroadcastChannel('sw-broadcast');
channel.postMessage(event.data.payload);
channel.close();
}
});
4.2 SharedWorker 广播
// SharedWorker
const connections = new Set();
self.onconnect = (e) => {
const port = e.ports[0];
connections.add(port);
port.onmessage = (event) => {
// 广播到所有连接
connections.forEach(p => {
if (p !== port) {
p.postMessage(event.data);
}
});
};
port.start();
};
五、注意事项
5.1 同源限制
// ❌ 不同源的标签页无法通信
// https://example.com 和 http://example.com 无法通信
const channel = new BroadcastChannel('shared');
// ✅ 同源即可,协议、域名、端口都相同
// https://example.com:443 和 https://example.com 可以通信
5.2 消息大小
// 消息会被结构化克隆算法序列化
// 大消息可能影响性能
channel.postMessage({ largeArray: new Array(1000000) }); // ⚠️ 可能卡顿
5.3 浏览器支持
// 检测支持
if ('BroadcastChannel' in window) {
console.log('BroadcastChannel supported');
}
六、面试高频问题
Q: BroadcastChannel 和 postMessage 有什么区别?
回答要点:postMessage 是点对点通信,需要知道目标窗口/iframe 的引用;BroadcastChannel 是发布-订阅模式,发送者不知道也不关心谁在接收。同源下多个标签页通信 BroadcastChannel 更简单,需要精确指定目标时用 postMessage。
Q: BroadcastChannel 可以跨域通信吗?
回答要点:不可以。BroadcastChannel 遵循同源策略,只有协议、域名、端口都相同的上下文才能通信。跨域通信需要使用 postMessage 并指定 targetOrigin。
Q: BroadcastChannel 适合什么场景?
回答要点:需要通知多个同源标签页的场景:主题切换同步、登录状态同步、购物车更新、缓存失效通知、实时协作应用中的状态同步等。