跨标签页通信神器:深入了解 BroadcastChannel 的妙用

最近在做项目遇到一个场景:在项目打开时候连接一个 websocket,在初始化、收到推送消息等场景下,其他页面需要同步进行一些操作,所以需要建立消息广播通知给需要的页面。下面是我想到的一些实现方案。

方案对比

方案 实现思路 推荐指数 优点 缺点
Pinia (Vue 项目) 使用 Vue 的状态管理工具 Pinia,在一个标签页中更新状态,其他标签页订阅状态变化并作出响应。 ⭐⭐⭐⭐ 集成 Vue 项目方便,状态管理强大 仅适用于 Vue 项目,需要额外配置
localStorage 或 sessionStorage 使用 localStoragesessionStorage 存储事件数据,并通过监听 storage 事件在其他标签页中接收消息。 ⭐⭐⭐ 简单易用,无需额外库 事件处理较为复杂,传递复杂数据有局限性
BroadcastChannel 使用 BroadcastChannel 创建频道,在不同标签页中发送和接收消息。 ⭐⭐⭐⭐⭐ 原生支持,多浏览上下文通信简单直观 仅支持同源页面,需要较新浏览器支持

考虑到项目中功能模块复用性,多个项目可能都会用到,所以方案1直接pass;去监听localStorage或sessionStorage也过于繁琐,所以也直接pass;由于是b端项目,无需考虑浏览器兼容的问题,而且BroadcastChannel 在绝大部分浏览器上是支持的,所以我们直接采用方案3即可。

BroadcastChannel介绍

BroadcastChannel 是一种 Web API,允许同一来源的不同浏览上下文(如标签页、iframe 或 Web worker)之间进行消息传递。这种机制使得在同一站点的不同页面之间同步数据变得非常简单和高效。

特性

  • 同源策略BroadcastChannel 只允许同一来源的页面之间进行通信。
  • 实时通信:消息传递是即时的,没有显著延迟。
  • 简洁易用:API 简单直观,易于上手。

使用场景

  • 多标签页同步:用户在一个标签页中进行的操作可以实时同步到其他标签页。
  • 跨 iframe 通信:在不同的 iframe 之间传递消息,无需通过父页面中转。
  • Web worker 通信:在主线程和多个 Web worker 之间实现高效通信。

示例

以下是一个使用 BroadcastChannel 进行消息传递的基本示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BroadcastChannel - Sender</title>
</head>
<body>
    <input type="text" id="inputBox">
    <button id="sendMessageButton">发送</button>
    <script>
        const channel = new BroadcastChannel('example_channel');
        document.getElementById('sendMessageButton').addEventListener('click', () => {
            const message = { type: 'update', content: document.getElementById('inputBox').value };
            channel.postMessage(message);
        });
    </script>
</body>
</html>

页面B接收消息:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BroadcastChannel - Receiver</title>
</head>
<body>
    <h1>接收消息</h1>
    <div id="messages"></div>
    <button id="closeMessage">断开</button>
    <script>
        const channel = new BroadcastChannel('example_channel');
        channel.onmessage = function(event) {
            const message = event.data;
            const messagesDiv = document.getElementById('messages');
            const messageElement = document.createElement('div');
            messageElement.textContent = `Type: ${message.type}, Content: ${message.content}`;
            messagesDiv.appendChild(messageElement);
        };
        document.getElementById('closeMessage').addEventListener('click', () => {
           channel.close();
        });
    </script>
</body>
</html>

通过以上代码,不同的浏览上下文(如不同标签页)可以在同一站点内通过 example_channel 频道进行消息传递,从而实现数据同步和通信;如果不再使用可以调用close()方法进行关闭,这样就可以让其被垃圾回收。

总结

通过对比不同的实现方案,最终选择使用 BroadcastChannel 作为跨标签页通信的解决方案。BroadcastChannel 的优势在于其原生支持,能够实现多浏览上下文之间的简单直观的通信,特别适用于需要高效实时通信的场景。尽管只支持同源页面且需要较新的浏览器支持,但在大多数现代浏览器上都能正常运行。

当然,我们还是根据项目需求去寻找最适合的技术方案,从项目角度出发。