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

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

  • 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))

往期年度总结

往期文章

专栏文章

结语

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

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

相关推荐
张3蜂4 小时前
Python 四大 Web 框架对比解析:FastAPI、Django、Flask 与 Tornado
前端·python·fastapi
南风知我意9574 小时前
【前端面试5】手写Function原型方法
前端·面试·职场和发展
qq_12498707534 小时前
基于Java Web的城市花园小区维修管理系统的设计与实现(源码+论文+部署+安装)
java·开发语言·前端·spring boot·spring·毕业设计·计算机毕业设计
摘星编程5 小时前
用React Native开发OpenHarmony应用:Image网络图片加载
javascript·react native·react.js
摘星编程5 小时前
OpenHarmony环境下React Native:ImageBase64图片显示
javascript·react native·react.js
阿蒙Amon5 小时前
TypeScript学习-第13章:实战与最佳实践
javascript·学习·typescript
小安驾到5 小时前
【前端的坑】vxe-grid表格tooltip提示框不显示bug
前端·vue.js
去码头整点薯条985 小时前
python第五次作业
linux·前端·python
沐墨染5 小时前
Vue实战:自动化研判报告组件的设计与实现
前端·javascript·信息可视化·数据分析·自动化·vue
奔跑的呱呱牛5 小时前
viewer-utils 图片预览工具库
javascript·vue·react