你还不会标签页之间的通信?

1,824 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

前言

大家有没有遇到过两个标签页或者浏览器窗口之间需要数据传输的业务需求?今天我们就来针对这个需求,展开一下实现方案吧!

BroadcastChannel

BroadcastChannel是浏览器提供的专门负责数据频道传输的API。它可以让同一个origin下的不同标签页,iframe甚至是窗口下的页面之间进行数据传输。

创建频道

首先创建一个新的频道

const channel = new BroadcastChannel('myChannel');

这里的入参是一个标识,用于指定频道。

数据监听

使用postMessage()发送数据

channel.postMessage(data);

数据监听

BroadcastChannel的postMessage跟常规的iframe之间的window.postMessage一样传输的内容会被浏览器结构化克隆算法序列化。因此我们需要留意处理无法序列化的数据。

channel.onmessage = function(e){
    console.log(e);
}
channel.onmessageerror= function(e){
    // 数据无法序列化时会触发messageerror事件
    console.log(e);
}

示例说明

下面用一个简单示例来示范一下效果。\

  1. 先创建一个空白的html文件。\
  2. 在body中写入下面的代码
    <button id="btn">click</button>
    <div id="msg"></div>
  1. 然后在下面加上脚本代码
    <script>
      const $btn = document.querySelector("#btn");
      const $msg = document.querySelector("#msg");
      const channel = new BroadcastChannel("test");

      channel.onmessage = (e) => {
        console.log(e);
        $msg.innerText = e.data;
      };

      $btn.addEventListener("click", () => {
        channel.postMessage("click!");
      });
    </script>
  1. 双击打开这个html,并复制同样的一个标签页。


现在我们有两个运行同样html的标签页了
5. 点击其中一个页面中的按钮,此时我们会发现另一个标签页中的$msg.innerText已经被修改了。


如果是两个浏览器窗口同样可以实现该效果

销毁频道

值得注意的是,当不需要使用频道时应该及时销毁,以释放这部分的内存。

channel.close();

兼容性

当然BroadcastChannel也不是万能的,跟大部分API一样有着兼容性的限制。下图是它的兼容性情况。

你也可以直接用代码判断当前的浏览器是否支持BroadcastChannel。

const ifBroadcastChannel = ()=> typeof window.BroadcastChannel === 'function';

StorageEvent

如果浏览器不支持BroadcastChannel的话,我们就没办法处理这个需求了吗? StorageEvent可以帮到你。
StorageEvent本质上是一个监听storage内容修改的事件API,在这里我们利用了localStorage本地持久化的特性也可以实现这个需求。

示例说明

直接上示例:

// dom 部分
    <button id="btn">click</button>
    <div id="msg"></div>
// JS部分
    <script>
      const $btn = document.querySelector("#btn");
      const $msg = document.querySelector("#msg");
      const channelName = '__myChannel';

      window.addEventListener("storage", (e) => {
        const key = e.key;
        if(key === channelName){
            $msg.innerText = e.newValue;
        }
      });

      $btn.addEventListener("click", () => {
        localStorage.setItem(channelName,'click!')
      });
    </script>

不同处

比较两者存在着明显的区别:

  1. BroadcastChannel创建一个频道示例传输数据,而StorageEvent则是利用监听事件。
  2. BroadcastChannel可以通过主动触发事件,而StorageEvent需要修改localStorage
  3. BroadcastChannel可以连续发送同样的信息,StorageEvent的触发必须是storage的内容修改,因此同样的信息不能连续触发事件。
  4. BroadcastChannel触发事件没有别的复作用,StorageEvent必须写入额外的localStorage,有可能造成数据冗余,需要主动删除。

兼容性

相比 BroadcastChannel, StorageEvent 的兼容性好得多,具体情况如下:

工具库

现在你已经掌握了页面间数据传输的底层原理了,但并不一定每次都需要自己实现这个效果。可以考虑使用现成的工具库。
sysend.js是一个专门实现这个业务需求的库,同时也会浏览器的兼容选择使用方案,有兴趣的朋友可以看一下。
github:github.com/jcubic/syse…
npmjs:www.npmjs.com/package/sys…

总结

今天我们从页面间通信的需要出发,掌握了 BroadcastChannel 已经 StorageEvent 两种底层实现方式。并了解了 sysend.js 库的使用。希望大家在之后遇到这个需求时可以灵活运用本文的知识。
如果觉得本文对你有一点帮助的话,麻烦给我点个赞吧~

参考

developer.mozilla.org/en-US/docs/…
developer.mozilla.org/zh-CN/docs/…