多页签通讯

262 阅读3分钟

使用背景

  • iframe 嵌入多个页签,页签之间通讯
  • a页面打开b页面,b页面提交了需要刷新a页面

1、onstorage

WindowEventHandlers.onstorage 属性包含一个在 storage 事件触发时运行的事件处理程序。当更改存储时会触发事件处理程序。

document.getElementById('l-btn').onclick = function () {     localStorage.setItem('storage1'Date.now())   }
                              
window.onstorage = function(e) {
  console.log(`The ${e.key}  key has been changed from ${e.oldValue}  to  ${e.newValue} .`);
};

2、open & opener

当我们 系统中通过 window.open 打开一个新页面时,window.open 方法会返回一个被打开页面的引用,而被打开页面则可以通过 window.opener 获取到打开它的页面的引用(当然这是在没有指定noopener的情况下)

  window.opener.postMessage(data);

  window.addEventListener('message'function (e) {
 
  });
</script>

3、BroadCast Channel

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

// 创建
const broadcastChannel = new BroadcastChannel('channelName');

// 监听消息
broadcastChannel.onmessage = function(e) {
    console.log('监听消息:', e.data);
};

// 发送消息
broadcastChannel.postMessage('测试:传送消息');

// 关闭
broadcastChannel.close();

4、Service Worker

Service workers 本质上充当 Web 应用程序、浏览器与网络(可用时)之间的代理服务器。

Service Worker 是一个可以长期运行在后台的 Worker,能够实现与页面的双向通信。多页面共享间的 Service Worker 可以共享,将 Service Worker 作为消息的处理中心(中央站)即可实现广播效果。

//-----./index.html
<div id="app"></div>
<button id="tab">新开 Tab</button>
<button id="l-btn">发送消息</button>
<script>
  /* 判断当前浏览器是否支持serviceWorker */
  if ('serviceWorker' in navigator) {
    /* 当页面加载完成就创建一个serviceWorker */
    window.addEventListener('load'function () {
      /* 创建并指定对应的执行内容 */
      /* scope 参数是可选的,可以用来指定你想让 service worker 控制的内容的子目录。在这个例子里,我们指定了 '/',表示 根网域下的所有内容。这也是默认值。*/
      navigator.serviceWorker.register('./serviceWorker.js', { scope'./' })
        .then(function (registration) {
        console.log('ServiceWorker registration successful with scope: ', registration.scope);
      })
        .catch(function (err) {
        console.log('ServiceWorker registration failed: ', err);
      });
    });

    // 监听消息
    navigator.serviceWorker.addEventListener('message'function (e) {
      const data = e.data;
      console.log('我接受到消息了:', data);
    });

    document.getElementById('l-btn').onclick = function () {
      navigator.serviceWorker.controller && navigator.serviceWorker.controller.postMessage('测试,传送消息,我发送消息啦。。。');
    };
  }
</script>

//-----./serviceWorker.js
/* 监听安装事件,install 事件一般是被用来设置你的浏览器的离线缓存逻辑 */
this.addEventListener('install'function (event) {
  /* 通过这个方法可以防止缓存未完成,就关闭serviceWorker */
  event.waitUntil(
    /* 创建一个名叫V1的缓存版本 */
    caches.open('v1').then(function (cache) {
      /* 指定要缓存的内容,地址为相对于跟域名的访问路径 */
      return cache.addAll(['./index.html']);
    })
  );
});

/* 注册fetch事件,拦截全站的请求 */
this.addEventListener('fetch'function (event) {
  event.respondWith(
    // magic goes here

    /* 在缓存中匹配对应请求资源直接返回 */
    caches.match(event.request)
  );
});

/* 监听消息,通知其他 Tab 页面 */
this.addEventListener('message'function(event) {
  this.clients.matchAll().then(function(clients) {
    clients.forEach(function(client) {
      // 这里的判断目的是过滤掉当前 Tab 页面,也可以使用 visibilityState 的状态来判断
      if(!client.focused) {
        client.postMessage(event.data)
      }
    })
  })
})

5、SharedWorker

SharedWorker 接口代表一种特定类型的 worker,可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通 worker 的接口,具有不同的全局作用域, SharedWorkerGlobalScope。

  • Shared Worker 在实现跨页面通信时的,它无法主动通知所有页面,需要刷新页面或者是定时任务来检查是否有新的消息。在例子中我是手动刷新的,当然可以使用 setInterval 来定时刷新
  // 创建共享线程对象
let worker = new SharedWorker("./sharedWorker.js");

// 手动启动端口
worker.port.start();

// 处理从 worker 返回的消息
 worker.port.onmessage = function (val) {
    ...
 };

详情见: 我知道的前端跨页面通信

欢迎关注我的前端自检清单,我和你一起成长