1. 什么是 BroadcastChannel?
BroadcastChannel 是 HTML5 提供的一个 Web API,它允许同源的不同浏览器上下文(Window, Tab, Frame, Worker)之间进行通信。
如果说 MessageChannel 是两个人之间的"打电话",那么 BroadcastChannel 就是所有人都在听的"收音机":
- MessageChannel:点对点,私密、高效。
- BroadcastChannel:一对多,所有调到相同频道的页面都能收到消息。
2. 核心用法:三步上手
使用 BroadcastChannel 非常简单,只需要三个步骤:创建频道 、发送消息 、监听消息。
2.1 创建频道
通过传入一个字符串(频道名称)来初始化。只要同源的上下文使用相同的名称,它们就能连接在一起。
js
// 连接到名为 'user_updates' 的频道
const channel = new BroadcastChannel('user_updates');
2.2 发送消息
调用 postMessage 方法,将数据广播出去。支持的数据结构遵循结构化克隆算法 (与 MessageChannel 一致)。
js
// 场景:用户在当前页点击了退出登录
channel.postMessage({ type: 'LOGOUT', timestamp: Date.now() });
2.3 监听消息
在其他同源页面或 Worker 中,监听 message 事件。
js
channel.onmessage = (event) => {
const data = event.data;
if (data.type === 'LOGOUT') {
console.log('收到广播:用户在其他页面退出登录');
// 执行跳转逻辑
window.location.href = '/login';
}
};
2.4 断开连接
当不再需要接收消息(如页面销毁)时,建议关闭连接以释放资源。
js
channel.close();
3. 实战场景:单点登录(SSO)同步
这是 BroadcastChannel 最经典的应用场景。我们需要保证用户在一个 Tab 页退出或登录时,其他所有 Tab 页都能同步状态。
封装一个简单的 AuthSync 类
js
// utils/authSync.js
const CHANNEL_NAME = 'auth_sync_channel';
class AuthSync {
constructor() {
this.channel = new BroadcastChannel(CHANNEL_NAME);
this.initListener();
}
initListener() {
this.channel.onmessage = (event) => {
const { type } = event.data;
switch (type) {
case 'LOGOUT':
this.handleRemoteLogout();
break;
case 'LOGIN':
this.handleRemoteLogin();
break;
}
}
}
// 发起退出广播
notifyLogout() {
this.channel.postMessage({ type: 'LOGOUT' });
this.localLogout();
}
localLogout() {
// 清除 Token, 跳转路由等...
console.log('本地退出执行');
}
handleRemoteLogout() {
alert('您已在其他页面退出登录,即将刷新');
this.localLogout();
}
destroy() {
this.channel.close();
}
}
export default new AuthSync();
4. 方案对比:BroadcastChannel vs Others
在前端通信的武器库中,除了 BroadcastChannel,还有 localStorage 事件和 window.postMessage。为什么首选 BroadcastChannel?
| 特性 | BroadcastChannel | localStorage (storage 事件) | window.postMessage | MessageChannel |
|---|---|---|---|---|
| 通信模式 | 广播 (一对多) | 广播 (一对多) | 点对点 (含跨域) | 点对点 (双向流) |
| 同源限制 | 是 | 是 | 否 (可跨域) | 是 |
| 数据传递 | 对象/结构化克隆 | 仅字符串 | 对象/结构化克隆 | 对象/结构化克隆 |
| 实现复杂度 | 极简 | 繁琐 (需序列化/反序列化) | 复杂 (需处理 Origin 安全) | 复杂 (需维护 Port) |
为什么它比 localStorage 好?
老派的做法是监听 window 的 storage 事件。但 storage 事件有两个痛点:
- 它只在同源的其他页面触发(当前页面修改 storage 不会触发自己的事件)。
- 它必须真正修改存储中的数据(仅仅是同步状态却污染了 LocalStorage 空间)。
- 数据只能存为字符串,传递对象需要
JSON.parse/stringify,性能较差。
BroadcastChannel 专为此设计,不涉及持久化,API 更加纯粹。
5. 注意事项与"坑"
尽管 BroadcastChannel 很强大,但在使用时仍需注意以下几点:
- 同源策略 只有协议、域名、端口完全一致的情况下,页面才能通过
BroadcastChannel通信。localhost:8080和localhost:3000是无法通信的。 - 自言自语问题 在同一个页面 上下文中,如果你创建了
BroadcastChannel并发送消息,该页面自己也会收到message事件。如果需要区分"自己发的"和"别人发的",需要在消息体中加入唯一的 ID 进行过滤。 - 兼容性
BroadcastChannel支持所有现代浏览器。但 IE 不支持 。如果你的项目还需要兼容 IE,需要使用 Polyfill 或者降级方案(例如回退到localStorage)。 - 内存泄漏 在 SPA(单页应用)中,如果在组件中创建了
BroadcastChannel,务必在组件卸载时调用close(),否则可能会导致内存无法回收。
6. 总结
MessageChannel解决了精准通信的问题(线程间、iframe 间)。BroadcastChannel解决了广域通信的问题(多 Tab 页状态同步)。
掌握这两个 API,基本上就覆盖了前端 90% 的非 HTTP 通信场景。下次当你遇到"多标签页数据不同步"的问题时,不要第一时间想到轮询,试试 BroadcastChannel 这个"广播电台"吧!