在用户访问网站难免需要打开多个标签页的场景,有时候打开多个标签页,就需要做一些信息同步,比如打开多个问卷调查标签,当一个标签页填写完,需要通知其他标签页进行提示已填写,或者多个标签页需要公用一个长连接,或者页面国际化切换需要通知其他页签进行语种切换等等。诸多场景都需要进行标签页通信,本文介绍几种常见的多标签页通信的方案,看官可以按照实际需求选择合适自己的方案。

window.opener

window.opener 返回打开当前窗口的那个窗口的引用,例如:在 TabA 中打开了 TabB,TabB 的 window.opener 返回 TabA 的 window,如果当前窗口不是由其他窗口打开的,则该属性返回 null。

index.html 代码:

<a href="/detail.html" target="_blank" rel="opener">详情页</a>
<button onclick="openDetail()">打开详情页</button>
<script>
    function openDetail() {
    window.open('/detail.html', 'detail');
  }
  window.onmessage = function (e) {
    if (e.origin === 'http://localhost:8080') {
      console.log('Message from detail.html', e.data);
    }
  };
</script>

detail.html 代码

<button onclick="sendMessage()">发送消息</button>
<script>
  console.log(window.opener);
  function sendMessage() {
    if (window.opener) {
      window.opener.postMessage('我是详情页', 'http://localhost:8080');
    }
  }
</script>

通过 index.html 页面打开 detail.html ,就可以在 detail.html 页面获取到 opener 对象给 index.html 发送消息。

window.opener 通信机制主要适用于父标签页与由其直接打开的子标签页之间的通信,不适合多个标签页进行通信,使用场景比较局限。下面几种方案比较通用。

StorageEvent

StorageEvent 在同一个域下的不同页面之间触发,如果A页面注册了 storage 监听事件,B页面操作 storage 对象,则A页面会触发 storage 回调。

78e55c8d-87ad-4909-be8a-e228d62e8bd3.png

StorageEvent 对象常用的属性

属性 含义
key 表示发生变动的键名。如果 storage 事件是由clear()方法引起,该属性返回null。
oldValue 改变之前的旧值。如果是新增元素,则为null。
newValue 改变之后的新值。如果是删除元素,则为null。
storageArea 该属性是一个引用,指向发生变化的sessionStorage或localStorage对象
url 触发这个改变事件的页面的URL
target 当前窗口对象

storage 对象包含 localStoragesessionStorage 对象,下面以 localStorage 对象为例:

// Tab 1:
window.addEventListener('storage', function (event) {
  if (event.key === 'language') {
    console.log(event.newValue);
  }
});

// Tab 2:
localStorage.setItem('language', 'zh-CN');

这时候在 Tab 1 就可以打印 zh-CN ,可以利用此方法来通知其他Tab选项卡进行一些额外的操作。StorageEvent 基本所有浏览器都可以支持,可以放心使用。

BroadcastChannel

BroadcastChannel 接口代理了一个命名频道,可以让指定相同源下的任意浏览器上下文来订阅它。它允许同源的不同浏览器窗口,Tab 页,frame 或者 iframe 下的不同文档之间相互通信。通过触发一个 message 事件,消息可以广播到所有监听了该频道的 BroadcastChannel 对象。

浏览器上下文指的是同源下的 tab选项卡,iframe等等。

a5a95ee7-67bd-4c84-bf01-7e6d86bfb467.webp
BroadcastChannel通信

废话不多说,直接上代码:

// Tab 1
const channel = new BroadcastChannel('myChannel');
channel.addEventListener('message', function (event) {
  if (event.data.key === 'myData') {
    console.log(event.data.value);
  }
});
const btn = document.getElementById('btn');
btn.addEventListener('click', function () {
  channel.postMessage({ key: 'myData', value: '你好,我是首页' });
});

// Tab 2
const channel = new BroadcastChannel('myChannel');
channel.addEventListener('message', function (event) {
  if (event.data.key === 'myData') {
    console.log(event.data.value);
  }
});
const btn = document.getElementById('btn');
btn.addEventListener('click', function () {
  channel.postMessage({ key: 'myData', value: '你好,我是详情页' });
});

上述代码,只要打开的页面,都可以通过 channel 发送消息给其他tab选项卡。Broad 使用起来也比较简单,但是还是少部分浏览器版本不支持。

上面介绍多标签页通信的实现,有类似的产品需求可以根据实际情况选择合适的方案。