跨窗口通信的九重天劫:从postMessage到BroadcastChannel
跨窗口通信的九重天劫:从postMessage到BroadcastChannel
第一重:postMessage 基础劫 —— 安全与效率的平衡术
// 父窗口发送
const child = window.open('child.html');
child.postMessage({ type: 'AUTH_TOKEN', token: 'secret' }, 'https://your-domain.com');
// 子窗口接收
window.addEventListener('message', (e) => {
if (e.origin !== 'https://parent-domain.com') return;
console.log('收到消息:', e.data);
});
安全守则:
- 始终验证
origin
属性 - 敏感数据使用
JSON.stringify
+加密
- 使用
transfer
参数传递大型二进制数据(如ArrayBuffer)
第二重:MessageChannel 双生劫 —— 高性能私有通道
// 建立通道
const channel = new MessageChannel();
// 端口传递
parentWindow.postMessage('INIT_PORT', '*', [channel.port2]);
// 接收端处理
channel.port1.onmessage = (e) => {
console.log('通过专用通道收到:', e.data);
};
// 发送消息
channel.port1.postMessage({ priority: 'HIGH', payload: data });
性能优势:
- 相比普通postMessage减少50%的序列化开销
- 支持传输10MB以上文件(Chrome实测)
第三重:BroadcastChannel 广播劫 —— 同源全域通信
// 发送方
const bc = new BroadcastChannel('app-channel');
bc.postMessage({ event: 'USER_LOGOUT' });
// 接收方
const bc2 = new BroadcastChannel('app-channel');
bc2.onmessage = (e) => {
if (e.data.event === 'USER_LOGOUT') {
localStorage.clear();
}
};
适用场景:
- 多标签页状态同步
- 全局事件通知系统
- 跨iframe配置更新
第四重:SharedWorker 共享劫 —— 持久化通信枢纽
// worker.js
const connections = [];
onconnect = (e) => {
const port = e.ports[0];
connections.push(port);
port.onmessage = (e) => {
connections.forEach(conn => {
if (conn !== port) conn.postMessage(e.data);
});
};
};
// 页面使用
const worker = new SharedWorker('worker.js');
worker.port.start();
worker.port.postMessage('来自页面的消息');
内存管理:
- 每个SharedWorker实例共享同一个全局作用域
- 需要手动清理断开连接的端口
第五重:localStorage 事件劫 —— 投机取巧的同步
// 页面A
localStorage.setItem('sync-data', JSON.stringify({
timestamp: Date.now(),
data: '重要更新'
}));
// 页面B
window.addEventListener('storage', (e) => {
if (e.key === 'sync-data') {
const data = JSON.parse(e.newValue);
console.log('跨页更新:', data);
}
});
致命缺陷:
- 事件仅在其他页面触发
- 同步API导致主线程阻塞
- 无法传递二进制数据
第六重:IndexedDB 观察劫 —— 数据库驱动通信
// 建立观察者
let lastVersion = 0;
const db = await openDB('msg-db', 1);
db.transaction('messages')
.objectStore('messages')
.openCursor().onsuccess = (e) => {
const cursor = e.target.result;
if (cursor && cursor.value.version > lastVersion) {
lastVersion = cursor.value.version;
handleMessage(cursor.value);
}
};
// 写入新消息
await db.add('messages', {
version: Date.now(),
content: '新订单通知'
});
适用场景:
- 需要持久化保存的通信记录
- 离线优先的跨窗口消息队列
第七重:Window.name 穿越劫 —— 上古秘术
// 页面A
window.name = JSON.stringify({ session: 'temp123' });
location.href = 'pageB.html';
// 页面B
const data = JSON.parse(window.name);
console.log('穿越传递:', data);
安全警告:
- 数据暴露在所有同源页面
- 最大容量约2MB
- 现代应用已不建议使用
第八重:Server-Sent Events (SSE) 服务劫 —— 服务器中转
// 服务端(Node.js)
app.get('/updates', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
setInterval(() => {
res.write(`data: ${Date.now()}\n\n`);
}, 1000);
});
// 浏览器端
const es = new EventSource('/updates');
es.onmessage = (e) => {
allWindows.forEach(w => w.postMessage(e.data));
};
架构优势:
- 支持跨设备同步
- 自动重连机制
- 与WebSocket互补(单向vs双向)
第九重:WebSocket 广播劫 —— 实时通信终极形态
// 共享连接管理
const wsMap = new Map();
function connectWS() {
const ws = new WebSocket('wss://push.your-app.com');
ws.onmessage = (e) => {
const data = JSON.parse(e.data);
if (data.type === 'BROADCAST') {
broadcastToAllTabs(data.payload);
}
};
return ws;
}
// 页面可见性控制
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
ws.close();
} else {
ws = connectWS();
}
});
性能优化:
- 心跳包维持连接(每30秒)
- 消息压缩(JSON → ArrayBuffer)
- 退避重连策略
渡劫指南(技术选型矩阵)
graph LR
A[是否需要持久化?] -->|是| B[IndexedDB]
A -->|否| C{实时性要求}
C -->|高| D[WebSocket]
C -->|中| E[BroadcastChannel]
C -->|低| F[postMessage]
B --> G[是否需要跨设备?]
G -->|是| H[SSE/WebSocket]
G -->|否| I[localStorage事件]
天劫问答
- 如何防止跨窗口消息风暴?
- 采用消息节流(throttle)
- 使用
window.performance.now()
标记时序 - 实施优先级队列
- 哪种方式最适合微前端架构?
BroadcastChannel
全局通信 +postMessage
父子隔离
- 如何实现跨源安全通信?
- 使用
iframe
作为代理中继 - 配合CORS和
document.domain
设置
调试工具推荐
- Charles - 抓取WebSocket消息
- Window Query - 查看所有窗口对象
- Postman - 模拟SSE事件流
性能检测代码:
// 通信延迟检测
const start = performance.now();
channel.postMessage('ping');
channel.onmessage = () => {
console.log('往返延迟:', performance.now() - start);
};
作者:ErpanOmer
来源:juejin.cn/post/7498619063671046196
来源:juejin.cn/post/7498619063671046196