优雅!原生Js实现多标签页之间的数据共享如此简单

大家好,我是石小石!一个热爱技术分享的开源社区贡献者,小册《油猴脚本实战指南》作者。


最近在开发油猴脚本中遇到一个场景:用户在多个标签页中打开了同一个 Web 应用时,A页面发送消息,其余所有页面能监听到消息(比如A页面退出,其余页面全部退出)。

如果你对网页脚本开发感兴趣,参考:为什么推荐前端学习油猴脚本开发?

原本使用的是GM_saveTab相关API实现,但后来发现,原生JS通过BroadcastChannellocalStorage + storage 事件也可以轻松解决多标签页之间数据共享的问题,简单记录一下。


使用 BroadcastChannel 进行标签页通信

什么是 BroadcastChannel?

BroadcastChannel 是一种浏览器原生提供的 API,用于在同源的不同标签页之间进行通信。它就像一个广播站,你可以在任意标签页中"发送消息",其他监听了这个频道的页面就能接收到。

使用示例

  • 创建频道
js 复制代码
const channel = new BroadcastChannel('my-channel');
  • 监听消息
js 复制代码
channel.onmessage = (e) => {
  console.log('收到消息:', e.data);
};
  • 发送消息
js 复制代码
channel.postMessage('你好,其他标签页!');
  • 关闭频道
js 复制代码
channel.close();

示例:多标签页实时消息广播

js 复制代码
<!DOCTYPE html>
<html>
  <head>
    <title>BroadcastChannel 示例</title>
  </head>
  <body>
    <h2>BroadcastChannel 演示</h2>
    <input id="msgInput" placeholder="输入要广播的消息" />
    <button onclick="sendMessage()">发送</button>

    <ul id="log"></ul>

    <script>
      const channel = new BroadcastChannel('demo_channel');
      const log = document.getElementById('log');

      channel.onmessage = (event) => {
        const li = document.createElement('li');
        li.textContent = `接收到消息: ${event.data}`;
        log.appendChild(li);
      };

      function sendMessage() {
        const input = document.getElementById('msgInput');
        channel.postMessage(input.value);
        input.value = '';
      }
    </script>
  </body>
</html>

实际应用场景:

  • 多标签页中保持消息通知一致
  • 实时登出同步(任一页面登出,其他页自动退出)
  • 多窗口之间状态广播(主题切换、设置同步等)

使用 localStorage + storage 事件监听同步

storage 事件

当我们想兼容老浏览器,或者不想引入 BroadcastChannel 时,localStorage 也可以用来实现通信。虽然它本质上是一个持久化存储工具,但在另一个标签页更改 localStorage 时,当前页面会触发 storage 事件:

标签页 A:更改localStorage

js 复制代码
localStorage.setItem('laoliu',JSON.stringify({
  type: '不想工作',
  text: "只要胆子大,天天都是假"
}) );

标签页 B:接收消息

js 复制代码
window.addEventListener('storage', (event) => {
  if (event.key === 'laoliu') {
    const msg = JSON.parse(event.newValue);
    if (msg.type === '不想工作') {
      alert('6666,你个老六');
    }
  }
});

我们可以利用这个特性来传递数据。

示例:使用 localStorage 进行数据同步

js 复制代码
<!DOCTYPE html>
  <html>
  <head>
  <title>localStorage + storage 事件</title>
  </head>
  <body>
  <h2>localStorage 通信演示</h2>
  <input id="msgInput" placeholder="输入要同步的消息" />
  <button onclick="syncMessage()">同步</button>

  <ul id="log"></ul>

  <script>
  const log = document.getElementById('log');

function syncMessage() {
  const input = document.getElementById('msgInput');
  localStorage.setItem('crossTabMessage', JSON.stringify({
    text: input.value,
    timestamp: Date.now()
  }));
  input.value = '';
}

window.addEventListener('storage', (event) => {
  if (event.key === 'crossTabMessage') {
    const data = JSON.parse(event.newValue);
    const li = document.createElement('li');
    li.textContent = `接收到消息: ${data.text}`;
    log.appendChild(li);
  }
});
</script>
  </body>
  </html>

注意事项:

  • storage 事件只在非当前修改页面触发。
  • 所以发送方不会接收到消息,接收方会响应。
  • 建议发送消息时携带 timestamp 等唯一标识,避免缓存误判。

特性对比

特性 BroadcastChannel localStorage + storage
是否跨标签页通信
是否跨域/跨源通信支持 ❌(同源) ❌(同源)
是否支持对象直接传输 ✅(会自动序列化) ❌(需手动序列化)
是否支持双向通信 ❌(发送方收不到)
浏览器兼容性 新(Chrome 54+,Safari 15+) 老(广泛兼容)

如果我们只支持现代浏览器,BroadcastChannel 是最佳选择,API 简洁、逻辑直观。需要兼容旧版浏览器时,localStorage 方案依然是个不错的替代。


一个完整的封装Demo

为了让项目更优雅地使用这些功能,我还封装了一个简单的通用消息发布器:

js 复制代码
class TabMessenger {
  constructor(channelName = 'default') {
    this.isBroadcastSupported = typeof BroadcastChannel !== 'undefined';
    this.channelName = channelName;

    if (this.isBroadcastSupported) {
      this.channel = new BroadcastChannel(channelName);
    } else {
      window.addEventListener('storage', this._onStorage.bind(this));
    }
  }

  onMessage(handler) {
    if (this.isBroadcastSupported) {
      this.channel.onmessage = (e) => handler(e.data);
    } else {
      this.storageHandler = handler;
    }
  }

  postMessage(msg) {
    if (this.isBroadcastSupported) {
      this.channel.postMessage(msg);
    } else {
      localStorage.setItem('__tab_msg_' + this.channelName, JSON.stringify({
        msg,
        ts: Date.now()
      }));
    }
  }

  _onStorage(e) {
    if (e.key.startsWith('__tab_msg_' + this.channelName)) {
      const data = JSON.parse(e.newValue);
      this.storageHandler?.(data.msg);
    }
  }
}

使用方式:

js 复制代码
const messenger = new TabMessenger('myApp');
messenger.onMessage((data) => {
  console.log('收到消息:', data);
});

messenger.postMessage('你好,其他页面!');

总结

我们可以看到,不需要复杂的框架和依赖,仅使用原生 API,我们就能轻松实现标签页之间的数据同步。在多标签页同步的场景中:

  • 首选 BroadcastChannel,简单清爽。
  • 备用 localStorage + storage,兼容好。
相关推荐
陈随易14 分钟前
AI新技术VideoTutor,幼儿园操作难度,一句话生成讲解视频
前端·后端·程序员
Pedantic17 分钟前
SwiftUI 按钮Button:完整教程
前端
前端拿破轮19 分钟前
2025年了,你还不知道怎么在vscode中直接调试TypeScript文件?
前端·typescript·visual studio code
代码的余温21 分钟前
DOM元素添加技巧全解析
前端
JSON_L24 分钟前
Vue 电影导航组件
前端·javascript·vue.js
用户214118326360232 分钟前
01-开源版COZE-字节 Coze Studio 重磅开源!保姆级本地安装教程,手把手带你体验
前端
大模型真好玩1 小时前
深入浅出LangChain AI Agent智能体开发教程(四)—LangChain记忆存储与多轮对话机器人搭建
前端·人工智能·python
帅夫帅夫1 小时前
深入理解 JWT:结构、原理与安全隐患全解析
前端
Struggler2811 小时前
google插件开发:如何开启特定标签页的sidePanel
前端
爱编程的喵2 小时前
深入理解JSX:从语法糖到React的魔法转换
前端·react.js