浏览器标签页间通信
实现同源标签页之间数据通信的多种方法
问题
如何实现浏览器内多个标签页之间的通信?
解答
Broadcast Channel
用于同源不同页面之间完成通信,在某个页面发送的消息会被其他页面监听到。注意该方法无法完成跨域的数据传输。
// 页面 A
const channel = new BroadcastChannel('my_channel');
channel.postMessage('Hello from A');
// 页面 B
const channel = new BroadcastChannel('my_channel');
channel.onmessage = (e) => {
console.log(e.data); // 'Hello from A'
};
localStorage
localStorage 是浏览器多个标签共用的存储空间,可以用来实现多标签之间的通信(session 是会话级的存储空间,每个标签页都是单独的)。
// 页面 A
localStorage.setItem('message', 'Hello');
// 页面 B
window.addEventListener('storage', (e) => {
if (e.key === 'message') {
console.log(e.newValue); // 'Hello'
}
});
SharedWorker
SharedWorker 可以被多个 window 共同使用,但必须保证这些标签页都是同源的(相同的协议、主机和端口号)。
// 创建 SharedWorker
const worker = new SharedWorker('worker.js');
// 页面 A
worker.port.postMessage('Hello from A');
// 页面 B
worker.port.onmessage = (e) => {
console.log(e.data);
};
WebSocket
全双工通信可以实现多个标签之间的通信,需要服务器支持。
const socket = new WebSocket('ws://localhost:8080');
socket.onmessage = (event) => {
console.log(event.data);
};
socket.send('Hello');
postMessage
适用于两个有依赖关系的标签页,如 A 页面通过 window.open 打开 B 页面,或 B 页面通过 iframe 嵌入至 A 页面。
// A 页面打开 B 页面
const windowB = window.open('b.html');
windowB.postMessage('Hello from A', '/');
// B 页面
window.addEventListener('message', (e) => {
let { data, source, origin } = e;
console.log(data); // 'Hello from A'
// B 页面回复 A 页面
source.postMessage('message echo', '/');
});
postMessage 的第一个参数为消息实体,它是一个结构化对象;第二个参数为消息发送范围选择器,设置为 '/' 意味着只发送消息给同源的页面,设置为 '*' 则发送全部页面。
setInterval + cookie
通过定时器不断检查 cookie 的值是否发生变化。由于 cookie 在同域可读,页面 B 改变 cookie 的值,页面 A 可以检测到。
// 页面 A
setInterval(() => {
const message = document.cookie.match(/message=([^;]+)/)?.[1];
if (message) {
console.log(message);
}
}, 1000);
// 页面 B
document.cookie = 'message=Hello';
这种方法相当浪费资源,不够优雅,不推荐使用。
关键点
- Broadcast Channel 和 localStorage 是最简单的同源通信方式
- SharedWorker 适合需要共享计算逻辑的场景
- postMessage 适用于有父子或 iframe 关系的页面
- WebSocket 需要服务器支持,但可以实现实时双向通信
- 避免使用 setInterval + cookie 的方式,性能差且不优雅
目录