大家好,我是石小石!一个热爱技术分享的开源社区贡献者,小册《油猴脚本实战指南》作者。
最近在开发油猴脚本中遇到一个场景:用户在多个标签页中打开了同一个 Web 应用时,A页面发送消息,其余所有页面能监听到消息(比如A页面退出,其余页面全部退出)。
如果你对网页脚本开发感兴趣,参考:为什么推荐前端学习油猴脚本开发?
原本使用的是GM_saveTab相关API实现,但后来发现,原生JS通过BroadcastChannel
和 localStorage + storage
事件也可以轻松解决多标签页之间数据共享的问题,简单记录一下。
使用 BroadcastChannel 进行标签页通信
什么是 BroadcastChannel?
BroadcastChannel
是一种浏览器原生提供的 API,用于在同源的不同标签页之间进行通信。它就像一个广播站,你可以在任意标签页中"发送消息",其他监听了这个频道的页面就能接收到。
使用示例
- 创建频道
js
const channel = new BroadcastChannel('my-channel');
- 监听消息
js
channel.onmessage = (e) => {
console.log('收到消息:', e.data);
};
- 发送消息
js
channel.postMessage('你好,其他标签页!');
- 关闭频道
js
channel.close();
示例:多标签页实时消息广播
js
<!DOCTYPE html>
<html>
<head>
<title>BroadcastChannel 示例</title>
</head>
<body>
<h2>BroadcastChannel 演示</h2>
<input id="msgInput" placeholder="输入要广播的消息" />
<button onclick="sendMessage()">发送</button>
<ul id="log"></ul>
<script>
const channel = new BroadcastChannel('demo_channel');
const log = document.getElementById('log');
channel.onmessage = (event) => {
const li = document.createElement('li');
li.textContent = `接收到消息: ${event.data}`;
log.appendChild(li);
};
function sendMessage() {
const input = document.getElementById('msgInput');
channel.postMessage(input.value);
input.value = '';
}
</script>
</body>
</html>

实际应用场景:
- 多标签页中保持消息通知一致
- 实时登出同步(任一页面登出,其他页自动退出)
- 多窗口之间状态广播(主题切换、设置同步等)
使用 localStorage + storage 事件监听同步
storage
事件
当我们想兼容老浏览器,或者不想引入 BroadcastChannel 时,localStorage
也可以用来实现通信。虽然它本质上是一个持久化存储工具,但在另一个标签页更改 localStorage
时,当前页面会触发 storage
事件:
标签页 A:更改localStorage
js
localStorage.setItem('laoliu',JSON.stringify({
type: '不想工作',
text: "只要胆子大,天天都是假"
}) );
标签页 B:接收消息
js
window.addEventListener('storage', (event) => {
if (event.key === 'laoliu') {
const msg = JSON.parse(event.newValue);
if (msg.type === '不想工作') {
alert('6666,你个老六');
}
}
});
我们可以利用这个特性来传递数据。
示例:使用 localStorage 进行数据同步
js
<!DOCTYPE html>
<html>
<head>
<title>localStorage + storage 事件</title>
</head>
<body>
<h2>localStorage 通信演示</h2>
<input id="msgInput" placeholder="输入要同步的消息" />
<button onclick="syncMessage()">同步</button>
<ul id="log"></ul>
<script>
const log = document.getElementById('log');
function syncMessage() {
const input = document.getElementById('msgInput');
localStorage.setItem('crossTabMessage', JSON.stringify({
text: input.value,
timestamp: Date.now()
}));
input.value = '';
}
window.addEventListener('storage', (event) => {
if (event.key === 'crossTabMessage') {
const data = JSON.parse(event.newValue);
const li = document.createElement('li');
li.textContent = `接收到消息: ${data.text}`;
log.appendChild(li);
}
});
</script>
</body>
</html>

注意事项:
storage
事件只在非当前修改页面触发。- 所以发送方不会接收到消息,接收方会响应。
- 建议发送消息时携带
timestamp
等唯一标识,避免缓存误判。
特性对比
特性 | BroadcastChannel | localStorage + storage |
---|---|---|
是否跨标签页通信 | ✅ | ✅ |
是否跨域/跨源通信支持 | ❌(同源) | ❌(同源) |
是否支持对象直接传输 | ✅(会自动序列化) | ❌(需手动序列化) |
是否支持双向通信 | ✅ | ❌(发送方收不到) |
浏览器兼容性 | 新(Chrome 54+,Safari 15+) | 老(广泛兼容) |
如果我们只支持现代浏览器,BroadcastChannel
是最佳选择,API 简洁、逻辑直观。需要兼容旧版浏览器时,localStorage
方案依然是个不错的替代。
一个完整的封装Demo
为了让项目更优雅地使用这些功能,我还封装了一个简单的通用消息发布器:
js
class TabMessenger {
constructor(channelName = 'default') {
this.isBroadcastSupported = typeof BroadcastChannel !== 'undefined';
this.channelName = channelName;
if (this.isBroadcastSupported) {
this.channel = new BroadcastChannel(channelName);
} else {
window.addEventListener('storage', this._onStorage.bind(this));
}
}
onMessage(handler) {
if (this.isBroadcastSupported) {
this.channel.onmessage = (e) => handler(e.data);
} else {
this.storageHandler = handler;
}
}
postMessage(msg) {
if (this.isBroadcastSupported) {
this.channel.postMessage(msg);
} else {
localStorage.setItem('__tab_msg_' + this.channelName, JSON.stringify({
msg,
ts: Date.now()
}));
}
}
_onStorage(e) {
if (e.key.startsWith('__tab_msg_' + this.channelName)) {
const data = JSON.parse(e.newValue);
this.storageHandler?.(data.msg);
}
}
}
使用方式:
js
const messenger = new TabMessenger('myApp');
messenger.onMessage((data) => {
console.log('收到消息:', data);
});
messenger.postMessage('你好,其他页面!');
总结
我们可以看到,不需要复杂的框架和依赖,仅使用原生 API,我们就能轻松实现标签页之间的数据同步。在多标签页同步的场景中:
- 首选 BroadcastChannel,简单清爽。
- 备用 localStorage + storage,兼容好。