书接上回,由于时间原因,还有几种情况没有总结,今天抽时间总结一下,其他情况请查看这里,众多跨标签页通信方式,你知道哪些?
以下则是常用的通信方法
BroadCast Channel
Service Worker
LocalStorage + window.onstorage 监听
Shared Worker 定时器轮询(setInterval)
IndexedDB 定时器轮询(setInterval)
cookie 定时器轮询(setInterval)
window.open + window.postMessage
(非同源也可)Websocket
(非同源也可)
IndexedDB 定时器轮询(setInterval)
- IndexedDB是一种底层的API,用于在客户端存储大量的结构化数据。
- 它是一种事务型数据库系统,是一种基于JavaScript面向对象数据库,有点类似于NoSQL(非关系型数据库)。
- IndexDB本身就是基于事务的,我们只需要指定数据库模式,打开与数据库的连接,然后检索和更新一系列事务即可。
下面就简单演示先通信的过程,就是在两个同源页面进行获取数据。
js
// 页面一
<button>新增</button>
<button>查询</button>
<button>删除</button>
<button>修改</button>
<script src="db.js"></script>
js
// 页面二
<script>
function openDB() {
return new Promise((resolve,reject) => {
const dbRequest = indexedDB.open("zhDB", 3)
let db = null
dbRequest.onsuccess = function(event) {
db = event.target.result
resolve(db)
}
})
}
const getAllData = async (db) => {
return new Promise((resolve, reject) => {
const transaction = db.transaction("users", "readwrite")
const store = transaction.objectStore("users")
const res = store.getAll()
res.onerror = () => {
reject([])
}
res.onsuccess = () => {
resolve(res.result)
}
})
}
let infoData = []
openDB().then(db => {
setInterval(async () => {
const data = await getAllData(db)
if(infoData.length !== data.length) {
infoData = data
console.log("infoData", infoData)
}
}, 1000)
})
</script>
[具体代码请访问](cross-page-communication/IndexDB at main · zhang-glitch/cross-page-communication · GitHub)
cookie 定时器轮询(setInterval)
mdn对于cookie各个属性及注意事项讲的非常清楚,可以在复习一下。cookie
这种跨页面通信方式也是在同源页面下才可以使用的。
js
// 页面一
<script>
document.cookie = "name=zh;path=/;domain=127.0.0.1;";
console.log("cookie", document.cookie)
</script>
js
// 页面二
<script>
// 获取指定cookie字段
function getCookie(key) {
return document.cookie.split(';').map(c => c.trim()).find(item => item.includes(`${key}=`))?.replace(`${key}=`, "")
}
const cookie = document.cookie
console.log("全部cookie", cookie)
let cookieName = ""
setInterval(() => {
if(getCookie("name") !== cookieName) {
console.log("前一次cookie name", cookieName)
console.log("当前cookie name", getCookie("name"))
cookieName = getCookie("name")
}
}, 1000)
</script>
window.open + window.postMessage
window.postMessage() 方法可以安全地实现跨源通信。所以这种方式也是一种可以在非同源的页面进行通信的方式。
这里用到了window.open
来打开一个页面,因为我们在向其他页面发送消息时,需要拿到目标页面的引用。具体请看mdn
通信的大致过程就是
- 我们通过
window.open
打开target页面。 - 获取到target页面引用,然后调用
postMessage
方法,该方法参数一是发送的数据,参数二是哪些源可以接收到该消息,该参数指定目标页面必须和指定的源协议,域名,端口号一致才可以监听到消息。 - 目标页面接收到消息后,对比
event.origin
,如果是发送方则可以使用postMessage
回复消息。
js
// 页面一
<button id="openBtn">打开index2.html窗口</button>
<input type="text" id="text">
<button id="btn">发送数据</button>
<script>
const text = document.querySelector("#text")
const btn = document.querySelector("#btn")
const openBtn = document.querySelector("#openBtn")
let index2Window = null
window.name = "页面一"
openBtn.onclick = () => {
// 使用hmserve库启动一个服务
index2Window = window.open("http://localhost:8888/index2.html")
}
btn.onclick = () => {
console.log("index2Window", index2Window)
// 发送的数据
// 指定哪些窗口能接收到消息事件
index2Window?.postMessage({
data: text.value
}, "http://localhost:8888")
}
// 监听数据的回复
window.addEventListener("message", (event) => {
if(event.origin.includes("http://localhost:8888")) {
console.log("event", event)
}else {
console.log("不安全数据")
}
})
</script>
js
// 页面二
<script>
window.name = "页面二"
window.addEventListener("message", (event) => {
// 为了数据安全,确定来源
if(event.origin.includes("http://127.0.0.1")) {
console.log("event", event)
// 回复数据
event.source.postMessage({
data: "回复数据"
}, event.origin)
}else {
console.log("不安全数据")
}
})
</script>
Websocket
在我们跨标签页通信中,WebSocket服务器就相当于中间人。就像Service Worker
,Shared Worker
通信方式一样,当我们接收到一个页面传递来的消息后,我们再将消息分发给其他页面。这也得益于websocket强大的服务器向客户端主动发送消息的能力。
这种方式也是可以在非同源页面进行通信的。
js
// 创建一个websocket服务器,这里使用ws快速创建
const {WebSocketServer} = require("ws")
// 创建wb服务器
const wbss = new WebSocketServer({
port: 3000
})
// 保存客户端链接实例
const clients = []
// 客户端连接时触发
wbss.on("connection", (client) => {
clients.push(client)
console.log("clients:1 客户端连接数", clients.length);
// 绑定当前客户端连接,获取消息
client.on('message', function message(data) {
console.log('data', data);
// 将消息传递给其他客户端
clients.forEach(c => {
if(c !== client) {
c.send(data)
}
})
});
// 客户端断开连接(关闭或者刷新)
client.on("close", () => {
const findIndex = clients.findIndex(c => c === client)
clients.splice(findIndex, 1)
console.log("clients:2 客户端连接数", clients.length);
})
})
js
// 页面一
<input type="text" id="text">
<button id="btn">发送数据</button>
<script>
const text = document.querySelector("#text")
const btn = document.querySelector("#btn")
// 和服务器建立连接
const ws = new WebSocket("ws://localhost:3000")
btn.onclick = () => {
ws.send(JSON.stringify({
data: text.value
}))
}
ws.onmessage = (event) => {
console.log("event", event)
}
// 关闭窗口或者刷新时关闭服务器连接
window.onbeforeunload = () => {
ws.close()
}
</script>
js
// 页面二
<script>
// 和服务器建立连接
const ws = new WebSocket("ws://localhost:3000")
ws.onmessage = (event) => {
console.log("event", event)
ws.send("我接收倒了消息")
}
// 关闭窗口或者刷新时关闭服务器连接
window.onbeforeunload = () => {
ws.close()
}
</script>
所有的案例代码请访问这里 zhang-glitch/cross-page-communication: 跨页面通信方案总结 (github.com))
总结
以上这八种方法梳理了常见的跨标签页面通信的过程,开发中一般对于同源页面使用LocalStorage + window.onstorage 监听
,BroadCast Channel
这两种方式,对于非同源页面使用window.open + window.postMessage
这种方式即可。其他方式了解即可。
其实这种通信方式有一些共性,indexedDB, cookie, localStorage
方式,他们都是浏览器存储方式,同源页面可以获取到这些内容,因此就可以进行通信。service worker, shared worker, websocket
他们都是通过一个中转人,通过接收消息,然后在进行消息分发。postMessage, BroadCast Channel
他们就是通过js内置的消息通信方式进行通信。
往期年度总结
往期文章
- 众多跨标签页通信方式,你知道哪些?
- 反调试吗?如何监听devtools的打开与关闭
- 因为原生,选择一家公司(前端如何防笔试作弊)
- 结合开发,带你熟悉package.json与tsconfig.json配置
- 如何优雅的在项目中使用echarts
- 如何优雅的做项目国际化
- 近三个月的排错,原来的憧憬消失喽
- 带你从0开始了解vue3核心(运行时)
- 带你从0开始了解vue3核心(computed, watch)
- 带你从0开始了解vue3核心(响应式)
- 3w+字的后台管理通用功能解决方案送给你
- 入职之前,狂补技术,4w字的前端技术解决方案送给你(vue3 + vite )
专栏文章
结语
本篇文章到此就结束了,欢迎在评论区交流。
🔥如果此文对你有帮助的话,欢迎💗关注 、👍点赞 、⭐收藏 、✍️评论, 支持一下博主~