多 tab 页通信方案

不使用 WebSocket 实现浏览器多标签页之间的通信

问题

需要在本地实现一个聊天室,多个 tab 页相互通信,不能使用 WebSocket,如何实现?

解答

方案一:LocalStorage + storage 事件

LocalStorage 可以在同源的不同标签页之间共享数据。当一个标签页修改 LocalStorage 时,其他标签页会触发 storage 事件。

// 发送消息
function sendMessage(message) {
  localStorage.setItem('chatMessage', JSON.stringify({
    content: message,
    timestamp: Date.now()
  }));
}

// 接收消息
window.addEventListener('storage', (e) => {
  if (e.key === 'chatMessage' && e.newValue) {
    const message = JSON.parse(e.newValue);
    console.log('收到消息:', message.content);
  }
});

方案二:Broadcast Channel API

Broadcast Channel API 专门用于同源页面之间的通信,API 更简洁直观。

// 创建频道
const channel = new BroadcastChannel('chat_room');

// 发送消息
channel.postMessage({
  user: 'User1',
  content: 'Hello',
  timestamp: Date.now()
});

// 接收消息
channel.onmessage = (e) => {
  console.log('收到消息:', e.data);
};

// 关闭频道
channel.close();

方案三:SharedWorker

SharedWorker 可以在多个标签页之间共享一个后台线程,适合需要集中管理状态的场景。

// shared-worker.js
const connections = [];

onconnect = (e) => {
  const port = e.ports[0];
  connections.push(port);
  
  port.onmessage = (event) => {
    // 广播给所有连接
    connections.forEach(conn => {
      conn.postMessage(event.data);
    });
  };
};

// 页面中使用
const worker = new SharedWorker('shared-worker.js');

worker.port.start();

// 发送消息
worker.port.postMessage({ content: 'Hello' });

// 接收消息
worker.port.onmessage = (e) => {
  console.log('收到消息:', e.data);
};

关键点

  • LocalStorage 方案最简单,但注意 storage 事件不会在当前页面触发,只在其他标签页触发
  • Broadcast Channel API 是最推荐的方案,API 简洁且专为跨标签页通信设计
  • SharedWorker 适合需要集中管理状态的复杂场景,但兼容性相对较差
  • 这些方案仅适用于同源页面的本地通信,无法实现跨域或跨网络通信