跨窗口通信的九重天劫:从postMessage到BroadcastChannel

821 阅读3分钟

跨窗口通信的九重天劫:从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);
});

安全守则

  1. 始终验证origin属性
  2. 敏感数据使用JSON.stringify + 加密
  3. 使用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事件]

天劫问答

  1. 如何防止跨窗口消息风暴?

    • 采用消息节流(throttle)
    • 使用window.performance.now()标记时序
    • 实施优先级队列
  2. 哪种方式最适合微前端架构?

    • BroadcastChannel全局通信 + postMessage父子隔离
  3. 如何实现跨源安全通信?

    • 使用iframe作为代理中继
    • 配合CORS和document.domain设置

调试工具推荐

  1. Charles - 抓取WebSocket消息
  2. Window Query - 查看所有窗口对象
  3. Postman - 模拟SSE事件流

性能检测代码

// 通信延迟检测
const start = performance.now();
channel.postMessage('ping');
channel.onmessage = () => {
  console.log('往返延迟:', performance.now() - start);
};