@author: 郭瑞峰
@createTime: 2024/01/31
@updateTime: 2024/02/01
前言
本来这篇文章应该在昨天写完的,结果咱的interview
耽搁了,嘿嘿。
好了,咱就直奔主题吧, 浏览器之间的页签可以相互通信,通信方式就是通过 BroadcastChannel
或者 SharedWorker
。当然你说localStorage
,也可以。不过用localStorage
这样写体现不出咱的逼格。 ο(=•ω<=)ρ⌒☆
至于web-socket
,抱歉,咱这说好不惊扰witch服务器的。
BroadcastChannel
下面介绍一下这个怎么用?
Google教你怎么用 度娘教你怎么用 mozilla教你怎么用 阮老师好像没写过,可能是我的姿势不对把
好了,介绍完了。相信看到这儿的大佬们都是在思考如何设计BroadcastChannel
如何操作,下面就是我实现需求的时候的设计。
消息共享
抱歉,这个不是我实现需求的思路,大家看看乐呵乐呵就行,要是不对的直接评论区教我做人就行了。
这个是最简单的使用方式,无需设置复杂逻辑。
javascript
import { cloneDeep } from 'lodash'
// 创建BroadcastChannel实例对象
const bc = new BroadcastChannel(window.location.host)
// 虽然是共享的,但还是建议使用uuid作为页签tag id
const uuid = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
// 共享的数据
let tagData = {
a: 1,
b: 2
}
bc.addEventListener('message', ({ data }) => {
switch (data.type) {
// 同步当前信息
case 'tagDataChange':
// 同步tagData数据
tagData = { ...tagData, ...data.data }
break
default:
break
}
})
const newData = {
a: 11,
b: 22
}
bc.postMessage({
type: 'tagDataChange',
origin: uuid,
aim: 'global',
// 注意,传递数据的时候不能直接写复杂变量名称,BroadcastChannel无法获取变量本身数据
data: cloneDeep(newData)
})
// 销毁
bc.close()
主次管控
这个是我实现需求的主体逻辑,设置主页签和子页签,并且利用 localStorage
和 sessionStorage
进行主次划分。
localStorage
:跨窗口同源数据存储,可以作为共享数据池 sessionStorage
: 不跨窗口数据存储,可以作为子页签数据存储
该怎么设计呢?算了,直接上我之前设计的规划图吧
这样规划就可以实现数据独立和相互之间的控制,但这个地方有个难点,就是没有缓存的情况下,并发访问,这个时候就是所有子页签夺权的时候。
这个时候就需要本页签状态管理,"锁"的消息和定时器进行**"立太子"**。
本来想把这部分设计代码发出来的,但根据公司签订的保密原则,得等我忙完年前的事儿后,写个demo让大家感受感受。 (;´д`)ゞ
最后,记得主页签关闭的时候进行数据迁移,所有同源页签关闭时记得销毁或处理localStorage里的公共数据池。
SharedWorker
这个时候就要介绍一下web-worker
概念:创建后在后台运行,不会阻塞主线程(如tag页签)。其中根据是否能同源共用分为Worker
(独享)和SharedWorker
(同源共享)。
所以说咱就需要使用SharedWorker
来设置一个共享数据池 ,亦或者是像上面一样,作为一个数据主控,防止夺权。
下面写个简单的例子:
JavaScript
// /static/count.worker.js
let count = 0;
const ports = []
self.onconnect = function(e) {
const port = e.ports[0];
ports.push(port)
port.onmessage = function(e) {
if (e.data === 'add') {
count++;
} else if (e.data === 'del') {
count--;
}
ports.forEach((item) => {
item.postMessage(count);
})
}
port.start()
}
html
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="count"></div>
<button id="btn_add">+</button>
<button id="btn_del">-</button>
<script>
// main script
const worker = new SharedWorker('/static/count.worker.js')
// 获取数据
worker.port.postMessage({ action: 'initial' })
// 接收数据
worker.port.onmessage = function(event) {
document.getElementById('count').innerHTML = event.data
}
// worker.port.start()
document.getElementById('btn_add').addEventListener('click', () => {
worker.port.postMessage('add')
})
document.getElementById('btn_del').addEventListener('click', () => {
worker.port.postMessage('del')
})
</script>
</body>
</html>
像这样打开两个页签点点看就知道了。
这样就可以将共有/共用资源存储在worker
线程里面,更新时候通过worker
所记录的端口ports
来进行遍历通知。
但请注意,若是对应页签移除,请记得消息通知 worker
,让ports
其及时清除记录。至于worker
销毁,可以交给浏览器,当使用worker
的页签全部关闭时候,浏览器会帮你销毁的。