返回

浏览器标签页通信的 JS 方案

前端

在日常业务开发中,时常会遇到这样一场景:浏览器标签页之间进行通信交互。比如有一个 list 页面,列表中的每一项数据经过点击后,以新打开浏览器 Tab 标签页方式显示这条数据的 detail 页面。类似的业务场景还有很多,诸如:

  • 购物网站中,商品详情页面的打开。
  • 管理后台系统中,某条记录详情页面的打开。
  • 社交软件中,新私信通知消息页面打开。

要实现标签页通信功能,本质上就是解决不同标签页中的脚本如何进行数据交换的问题。

传统的解决思路是借助 window.postMessage 方法,通过向目标标签页发送 message 事件来实现通信。接收方通过监听 message 事件来接收数据。

// 发送方
window.postMessage({ type: 'data-transfer', data: 'value' }, '*');

// 接收方
window.addEventListener('message', function(e) {
  if (e.data.type === 'data-transfer') {
    // 处理数据
  }
}, false);

这种方式虽然简单易用,但存在以下几个问题:

  • 只能在同源标签页间进行通信,跨域标签页间通信受限。
  • 无法传递复杂的数据类型,比如 FunctionObject 等。
  • 无法传递文件类型数据。

针对这些问题,业界也提出了一些解决方案,如:

  • 使用 IndexedDB 或 LocalStorage 实现跨域通信。
  • 使用 JSONP 实现跨域通信。
  • 使用 WebSockets 实现复杂数据类型和文件类型数据的传递。

然而,这些方案的实现相对复杂,而且在某些情况下仍有局限性。

针对以上问题,本文提出一种基于 BroadcastChannel 的标签页通信方案。

BroadcastChannel 是 HTML5 中引入的一个 API,它允许在不同的标签页(包括跨域标签页)之间建立双向通信通道。它的优点是:

  • 可以跨域通信。
  • 可以传递复杂的数据类型,包括 FunctionObject 和文件类型数据。
  • 使用简单,无需繁琐的配置和监听事件。

实现方式如下:

// 创建一个 BroadcastChannel 对象
const channel = new BroadcastChannel('my-channel');

// 发送方
channel.postMessage({ type: 'data-transfer', data: 'value' });

// 接收方
channel.onmessage = function(e) {
  if (e.data.type === 'data-transfer') {
    // 处理数据
  }
};

这种方案不仅解决了传统 window.postMessage 方法存在的跨域和数据类型传递受限的问题,而且实现简单,易于使用。

示例代码:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  
</head>
<body>
  <button onclick="openDetail()">打开详情页</button>

  <script>
    // 创建一个 BroadcastChannel 对象
    const channel = new BroadcastChannel('my-channel');

    // 发送方
    function openDetail() {
      channel.postMessage({ type: 'data-transfer', data: { id: 1, name: 'John Doe' } });

      // 打开新标签页
      window.open('detail.html');
    }

    // 接收方
    channel.onmessage = function(e) {
      if (e.data.type === 'data-transfer') {
        // 处理数据
        const data = e.data.data;
        console.log(`ID: ${data.id}, Name: ${data.name}`);
      }
    };
  </script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  
</head>
<body>
  <h1>详情页</h1>

  <script>
    // 创建一个 BroadcastChannel 对象
    const channel = new BroadcastChannel('my-channel');

    // 接收方
    channel.onmessage = function(e) {
      if (e.data.type === 'data-transfer') {
        // 处理数据
        const data = e.data.data;
        console.log(`ID: ${data.id}, Name: ${data.name}`);
      }
    };
  </script>
</body>
</html>

总结:

本文介绍了基于 BroadcastChannel 的标签页通信方案,这种方案跨域、支持复杂数据类型传递,而且实现简单,易于使用。通过本文的讲解和示例代码,开发者可以快速掌握该方案的应用,解决业务开发中遇到的标签页通信需求。