再不借用服务器资源的情况下,如何实现和规划同源页签通信相关东西

我的博客原文

@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()

主次管控

这个是我实现需求的主体逻辑,设置主页签和子页签,并且利用 localStoragesessionStorage 进行主次划分。

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的页签全部关闭时候,浏览器会帮你销毁的。

相关推荐
Jiaberrr3 小时前
前端实战:使用JS和Canvas实现运算图形验证码(uniapp、微信小程序同样可用)
前端·javascript·vue.js·微信小程序·uni-app
everyStudy3 小时前
JS中判断字符串中是否包含指定字符
开发语言·前端·javascript
城南云小白3 小时前
web基础+http协议+httpd详细配置
前端·网络协议·http
前端小趴菜、3 小时前
Web Worker 简单使用
前端
web_learning_3213 小时前
信息收集常用指令
前端·搜索引擎
tabzzz4 小时前
Webpack 概念速通:从入门到掌握构建工具的精髓
前端·webpack
200不是二百4 小时前
Vuex详解
前端·javascript·vue.js
滔滔不绝tao4 小时前
自动化测试常用函数
前端·css·html5
码爸4 小时前
flink doris批量sink
java·前端·flink
深情废杨杨4 小时前
前端vue-父传子
前端·javascript·vue.js