Loading... > @author: 郭瑞峰 > > @createTime: 2024/01/31 > > @updateTime: 2024/02/01 ### 前言 本来这篇文章应该在昨天写完的,结果咱的`interview`耽搁了,嘿嘿。 好了,咱就直奔主题吧, 浏览器之间的页签可以相互通信,通信方式就是通过 `BroadcastChannel` 或者 `SharedWorker`。当然你说`localStorage`,也可以。不过用`localStorage`这样写体现不出咱的逼格。 ο(=•ω<=)ρ⌒☆ 至于`web-socket`,抱歉,咱这说好不惊扰~~`witch`~~服务器的。  # BroadcastChannel 下面介绍一下这个怎么用? [Google教你怎么用](https://www.google.com/search?q=%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8BroadcastChannel "Google教你怎么用") [度娘教你怎么用](https://www.baidu.com/s?wd=%E5%A6%82%E4%BD%95%E7%94%A8BroadcastChannel "度娘叫你怎么用") [mozilla教你怎么用](https://developer.mozilla.org/zh-CN/docs/Web/API/BroadcastChannel "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`的页签全部关闭时候,浏览器会帮你销毁的。  最后修改:2024 年 11 月 27 日 © 允许规范转载 赞 0 如果觉得我的文章对你有用,请随意赞赏