其实在开发中多标签页通信也是比较常见的,以前开发中就遇见过,在当前页面操作完毕后刷新其他标签页,具体看这里,下面我们再来详细总结一下常用的通信方法。
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))
往期年度总结
往期文章
- 反调试吗?如何监听devtools的打开与关闭
- 因为原生,选择一家公司(前端如何防笔试作弊)
- 结合开发,带你熟悉package.json与tsconfig.json配置
- 如何优雅的在项目中使用echarts
- 如何优雅的做项目国际化
- 近三个月的排错,原来的憧憬消失喽
- 带你从0开始了解vue3核心(运行时)
- 带你从0开始了解vue3核心(computed, watch)
- 带你从0开始了解vue3核心(响应式)
- 3w+字的后台管理通用功能解决方案送给你
- 入职之前,狂补技术,4w字的前端技术解决方案送给你(vue3 + vite )
专栏文章
结语
本篇文章到此就结束了,欢迎在评论区交流。
🔥如果此文对你有帮助的话,欢迎💗关注 、👍点赞 、⭐收藏 、✍️评论, 支持一下博主~