众多跨标签页通信方式,你知道哪些?

其实在开发中多标签页通信也是比较常见的,以前开发中就遇见过,在当前页面操作完毕后刷新其他标签页,具体看这里,下面我们再来详细总结一下常用的通信方法。

  • BroadCast Channel
  • Service Worker
  • LocalStorage + window.onstorage 监听
  • Shared Worker 定时器轮询(setInterval)
  • IndexedDB 定时器轮询(setInterval)
  • cookie 定时器轮询(setInterval)
  • window.open + window.postMessage
  • Websocket

BroadCast Channel

BroadCast Channel可以帮我们创建一个用于广播的通信频道。当所有页面都监听同一频道的消息时,其中某一个页面通过它发送的消息就会被其他所有页面收到,但是前提是同源页面。

我们在通过BroadcastChannel创建通道时需要传递一个名称,表示同源下浏览器上下文唯一标识。我们在完成通信后还需要断开通道,让其被垃圾回收。因为浏览器没有其他方式知道频道不再被需要。

js 复制代码
// 页面1
<input type="text" id="text">
<button id="btn">发送数据</button>
<script>
const text = document.querySelector("#text")
const btn = document.querySelector("#btn")

// 创建频道
const broadCastChannel = new BroadcastChannel("broadcastChannel way")
btn.onclick = () => {
  // 可以直接传递对象,无需转化成json
  broadCastChannel.postMessage({
    sendMessage: text.value
  })
  // 关闭通道
  broadCastChannel.close();
}
</script>
js 复制代码
// 页面2
<script>
const channel = new BroadcastChannel("broadcastChannel way");
// 监听消息的发送
channel.addEventListener("message", (event) => {
  console.log("event", event) // event.data就是监听的消息
  // 关闭通道
  channel.close();
});
</script>

Service Worker

Service Worker实际上是浏览器和服务器之间的代理服务器,它最大的特点是在页面中注册并安装成功后,运行于浏览器后台,不受页面刷新的影响,可以监听和截拦作用域范围内所有页面的 HTTP 请求。

Service worker 运行在 worker 上下文:因此它无法访问 DOM,相对于驱动应用的主 JavaScript 线程,它运行在其他线程中,所以不会造成阻塞。它被设计为完全异步;因此,同步 XHR) 和 Web Storage 不能在 service worker 中使用。

它主要的作用是用来进行离线资源缓存的,以前写过一篇文章,可以看这里PWA技术

Service Worker 的目的在于离线缓存,转发请求和网络代理。我们在进行消息传递时,通信页面也必须同源。

js 复制代码
// 页面一
<input type="text" id="text">
<button id="btn">发送消息</button>
<script>
const btn = document.querySelector("#btn")
const text = document.querySelector("#text")
// 注册service worker
navigator.serviceWorker.register("./sw.js").then(res => {
  console.log("页面一:sw注册成功!")
})
btn.onclick = () => {
  // 发送消息
  navigator.serviceWorker.controller.postMessage({
    sendMessage: text.value
  })
}
</script>
js 复制代码
// 页面二
<script>
navigator.serviceWorker.register("./sw.js").then(res => {
  console.log("页面二:sw注册成功!")
})
navigator.serviceWorker.addEventListener("message", event => {
  console.log("event", event)
})
</script>
js 复制代码
// service worker注册文件 sw.js
// 消息监听
self.addEventListener("message", async (event) => {
  console.log("sw: event", event)
  // 获取所有注册了service worker客户端
  const clients = await self.clients.matchAll()
  console.log("注册的所有service worker!", clients)

  clients.forEach(client => {
    // 获取到数据进行传递
    client.postMessage({
      swMessage: event.data.sendMessage
    })
  })
})

如果两个页面不同源,我们来看看效果,由于vscode插件live server插件只能同时启动一个服务器,所以这里安利一下我自己写的静态资源服务器hmserve,全局安装npm install -g hmserve即可使用。开启一个新的服务。

由此可见页面二并没有监听到。所以采用Service Worker通信页面也需要同源。

这种方式进行通信,service worker文件就是一个中转站,分发消息的。

LocalStorage + window.onstorage 监听

这种跨标签页通信方式页面也必须同源。

这种方式具体可以看下面两篇文章

Shared Worker 定时器轮询(setInterval)

SharedWorker 接口代表一种特定类型的 worker,可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通 worker 的接口,具有不同的全局作用域。如果使用sharedWorker连接多个不同的页面,这些页面也必须同源。

worker文件就是监听各个页面传递的消息,然后进行分发消息。

js 复制代码
// 页面一
<input type="text" id="text">
<button id="btn">发送消息</button>
<script>
const btn = document.querySelector("#btn")
const text = document.querySelector("#text")
// 创建一个worker
const worker = new SharedWorker("worker.js")

btn.onclick = () => {
  console.log("发送消息")
  worker.port.postMessage({
    type: "first",
    sendMessage: text.value
  })
}
</script>
js 复制代码
// 页面二
<script>
const worker = new SharedWorker("worker.js")
worker.port.start()
worker.port.addEventListener("message", (event) => {
  if(event.data?.type === "first") { // 页面一发送的消息
      console.log("页面二:event", event)
  }
})

// 轮询向worker(中转站)发送消息
setInterval(() => {
  console.log("一直在轮询...")
  worker.port.postMessage({
    type: "second"
  })
}, 1000)
</script>
js 复制代码
// worker.js 他就是处理各个页面传递的消息,然后分配消息给指定页面。
// 消息监听
let data = null
onconnect = (event) => {
  const port = event.ports[0];

  // 页面都向worker发送消息,内部逻辑进行区分,然后回复消息。
  port.addEventListener("message", function (e) {
    if(e.data.type === "second") { // 页面二发送的消息
      port.postMessage(data)
      data = null
    }else if(e.data.type === "first"){ // 页面一发送的消息
      data = {
        type: "first",
        message: e.data
      }
    }
  });

  port.start(); 
}

由于时间原因,剩下几种方式下次再进行总结,感兴趣的小伙伴也可以自己查阅资料。

所有的案例代码请访问这里 zhang-glitch/cross-page-communication: 跨页面通信方案总结 (github.com))

往期年度总结

往期文章

专栏文章

结语

本篇文章到此就结束了,欢迎在评论区交流。

🔥如果此文对你有帮助的话,欢迎💗关注 、👍点赞 、⭐收藏✍️评论, 支持一下博主~

相关推荐
时差953几秒前
【面试题】Hive 查询:如何查找用户连续三天登录的记录
大数据·数据库·hive·sql·面试·database
web行路人7 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱0018 分钟前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
子非鱼92126 分钟前
【Ajax】跨域
javascript·ajax·cors·jsonp
超雄代码狂29 分钟前
ajax关于axios库的运用小案例
前端·javascript·ajax
长弓三石37 分钟前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
小马哥编程38 分钟前
【前端基础】CSS基础
前端·css
嚣张农民1 小时前
推荐3个实用的760°全景框架
前端·vue.js·程序员
周亚鑫1 小时前
vue3 pdf base64转成文件流打开
前端·javascript·pdf
落魄小二1 小时前
el-table 表格索引不展示问题
javascript·vue.js·elementui