多标签页通讯方案
使用 localStorage、SharedWorker、BroadcastChannel 实现标签页间通信
问题
如何实现同源网页多标签页(Tab)之间的通讯?
解答
方案一:localStorage + storage 事件
利用 localStorage 变化时触发的 storage 事件实现跨标签页通信。
// 发送消息
function sendMessage(data) {
localStorage.setItem('message', JSON.stringify({
data,
timestamp: Date.now() // 加时间戳确保每次都能触发事件
}))
}
// 接收消息(其他标签页)
window.addEventListener('storage', (e) => {
if (e.key === 'message' && e.newValue) {
const { data } = JSON.parse(e.newValue)
console.log('收到消息:', data)
}
})
// 使用
sendMessage({ type: 'login', userId: 123 })
注意:storage 事件只在其他标签页触发,当前页面不会触发。
方案二:BroadcastChannel
专门用于同源页面间广播通信的 API,使用最简单。
// 创建频道(所有标签页使用相同的频道名)
const channel = new BroadcastChannel('my-channel')
// 发送消息
channel.postMessage({ type: 'update', content: 'hello' })
// 接收消息
channel.onmessage = (e) => {
console.log('收到消息:', e.data)
}
// 关闭频道
channel.close()
方案三:SharedWorker
多个标签页共享同一个 Worker 实例,通过它中转消息。
// shared-worker.js
const ports = []
self.onconnect = (e) => {
const port = e.ports[0]
ports.push(port)
port.onmessage = (e) => {
// 广播给所有连接的标签页
ports.forEach(p => {
if (p !== port) { // 不发给自己
p.postMessage(e.data)
}
})
}
}
// 页面中使用
const worker = new SharedWorker('shared-worker.js')
// 发送消息
worker.port.postMessage({ type: 'notify', msg: 'hello' })
// 接收消息
worker.port.onmessage = (e) => {
console.log('收到消息:', e.data)
}
// 必须调用 start(如果没用 onmessage 而是用 addEventListener)
worker.port.start()
方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| localStorage | 兼容性好,简单 | 只能传字符串,有容量限制 |
| BroadcastChannel | API 简洁,支持任意数据 | IE 不支持 |
| SharedWorker | 可做复杂逻辑处理 | 实现复杂,兼容性一般 |
其他方案
// 方案四:postMessage + window.open
// 适用于父页面打开子页面的场景
const child = window.open('child.html')
child.postMessage('hello', '*')
// 子页面接收
window.addEventListener('message', (e) => {
console.log(e.data)
})
关键点
- localStorage 的
storage事件只在其他标签页触发,当前页面不触发 - BroadcastChannel 是最简洁的方案,推荐优先使用
- SharedWorker 适合需要共享状态或复杂逻辑的场景
- 所有方案都要求同源(协议、域名、端口相同)
- postMessage 可用于有引用关系的窗口间通信
目录