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

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

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

往期年度总结

往期文章

专栏文章

结语

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

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

相关推荐
喜樂的CC8 分钟前
[react]Next.js之自适应布局和高清屏幕适配解决方案
javascript·react.js·postcss
天天扭码22 分钟前
零基础 | 入门前端必备技巧——使用 DOM 操作插入 HTML 元素
前端·javascript·dom
软件测试曦曦27 分钟前
16:00开始面试,16:08就出来了,问的问题有点变态。。。
自动化测试·软件测试·功能测试·程序人生·面试·职场和发展
咖啡虫1 小时前
css中的3d使用:深入理解 CSS Perspective 与 Transform-Style
前端·css·3d
烛阴1 小时前
手把手教你搭建 Express 日志系统,告别线上事故!
javascript·后端·express
拉不动的猪1 小时前
设计模式之------策略模式
前端·javascript·面试
旭久1 小时前
react+Tesseract.js实现前端拍照获取/选择文件等文字识别OCR
前端·javascript·react.js
独行soc1 小时前
2025年常见渗透测试面试题-红队面试宝典下(题目+回答)
linux·运维·服务器·前端·面试·职场和发展·csrf
uhakadotcom2 小时前
Google Earth Engine 机器学习入门:基础知识与实用示例详解
前端·javascript·面试
麓殇⊙2 小时前
Vue--组件练习案例
前端·javascript·vue.js