前端面试题:浏览器两个 tab 都是同域,如何在一个tab 向另一个 tab 发消息

在浏览器中,两个同域的标签页(Tab)之间通信有多种实现方式,以下是常见的几种方法及其原理和代码示例:


1. 使用 localStorage 事件

当修改 localStorage 时,会触发 storage 事件,其他同源页面可以监听该事件实现通信。

javascript 复制代码
// Tab A 发送消息
localStorage.setItem('message', JSON.stringify({ content: 'Hello Tab B!' }));

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

特点

  • 只能传递字符串,需手动序列化/反序列化。
  • 当前 Tab 修改数据时不会触发自己的监听器,只有其他 Tab 会收到事件。
  • 受同源策略限制,仅限同域页面。

2. BroadcastChannel API

现代浏览器提供的专用通信 API,允许同源页面通过命名频道通信。

javascript 复制代码
// Tab A 发送消息
const channel = new BroadcastChannel('my_channel');
channel.postMessage({ content: 'Hello Tab B!' });

// Tab B 接收消息
const channel = new BroadcastChannel('my_channel');
channel.onmessage = (e) => {
  console.log('Received:', e.data);
};

特点

  • 直接支持结构化数据(对象、数组等)。
  • 需手动关闭频道(channel.close())。
  • 兼容性良好(支持 Chrome 54+、Firefox 38+、Edge 79+)。

3. 通过 window.postMessage(需窗口引用)

如果两个 Tab 存在引用关系(如通过 window.open 打开),可直接通过 postMessage 通信。

javascript 复制代码
// Tab A 打开 Tab B 并发送消息
const newTab = window.open('https://same-domain.com/tabB');
newTab.postMessage('Hello Tab B!', 'https://same-domain.com');

// Tab B 接收消息
window.addEventListener('message', (e) => {
  if (e.origin === 'https://same-domain.com') {
    console.log('Received:', e.data);
  }
});

特点

  • 需要直接引用目标窗口对象(如通过 window.openwindow.opener)。
  • 适用于有明确父子关系的页面。

4. Service Worker 作为中介

通过 Service Worker 作为消息中转站,实现跨 Tab 通信。

javascript 复制代码
// Tab A 发送消息
navigator.serviceWorker.controller.postMessage({
  type: 'MSG_TO_OTHER_TABS',
  data: 'Hello from Tab A!'
});

// Service Worker 代码(sw.js)
self.addEventListener('message', (event) => {
  if (event.data.type === 'MSG_TO_OTHER_TABS') {
    self.clients.matchAll().then(clients => {
      clients.forEach(client => {
        if (client.id !== event.source.id) {
          client.postMessage(event.data);
        }
      });
    });
  }
});

// Tab B 接收消息
navigator.serviceWorker.addEventListener('message', (event) => {
  console.log('Received:', event.data);
});

特点

  • 支持离线场景和复杂通信逻辑。
  • 需要注册 Service Worker(navigator.serviceWorker.register('sw.js'))。

总结对比

方法 是否需要窗口引用 数据传输类型 适用场景
localStorage 字符串 简单消息,兼容性要求高
BroadcastChannel 结构化数据 现代浏览器,无需窗口引用
window.postMessage 任意数据 有明确窗口引用的场景
Service Worker 任意数据 复杂场景(如离线、后台同步)

选择方案时需根据具体需求(如兼容性、数据复杂度、窗口关系)权衡。通常推荐优先使用 BroadcastChannellocalStorage 事件。

相关推荐
天天扭码6 小时前
如何实现流式输出?一篇文章手把手教你!
前端·aigc·ai编程
前端 贾公子6 小时前
vue移动端适配方案 === postcss-px-to-viewport
前端·javascript·html
GISer_Jing8 小时前
AI营销增长:4大核心能力+前端落地指南
前端·javascript·人工智能
GSDjisidi8 小时前
东京IT软件会社-(株)GSD|多种技术栈募集,高度人才+20分
开发语言·面试·职场和发展
明远湖之鱼8 小时前
一种基于 Service Worker 的渐进式渲染方案的基本原理
前端
前端小端长9 小时前
Vue 中 keep-alive 组件的原理与实践详解
前端·vue.js·spring
FeelTouch Labs9 小时前
Nginx核心架构设计
运维·前端·nginx
雪球工程师团队9 小时前
别再“苦力”写后台,Spec Coding “跑” 起来
前端·ai编程
m0_471199639 小时前
【场景】前端怎么解决离线收银、数据同步异常等场景问题
前端·javascript