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

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

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

往期年度总结

往期文章

专栏文章

结语

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

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

相关推荐
人工智能AI技术12 分钟前
计算机专业面试必看!90%学生都踩过的算法面雷区
人工智能·面试
reembarkation1 小时前
vue3中使用howler播放音频列表
前端·vue.js·音视频
手握风云-1 小时前
基于 Java 的网页聊天室(三)
服务器·前端·数据库
xlp666hub1 小时前
深度剖析 Linux Input 子系统(3):从零写一个 Input 驱动,最详细手把手(附完整代码)
linux·面试
weixin199701080161 小时前
《识货商品详情页前端性能优化实战》
前端·性能优化
Forever7_1 小时前
重磅!Vue3 手势工具正式发布!免费使用!
前端·前端框架·前端工程化
用户806138166591 小时前
发布为一个 npm 包
前端·javascript
Cosolar1 小时前
吃透这5种Agent模式,搞定智能体开发
人工智能·面试·全栈
树上有只程序猿2 小时前
低代码何时能出个“秦始皇”一统天下?我是真学不动啦!
前端·后端·低代码
TT_哲哲2 小时前
小程序双模式(文件 / 照片)上传组件封装与解析
前端·javascript