vue-使用Worker实现多标签页共享一个WebSocket

文章目录

  • 前言
  • 一、SharedWorker 是什么
    • SharedWorker 是什么
    • SharedWorker 的使用方式
    • SharedWorker 标识与独占
  • 二、Demo使用
  • 三、使用SharedWorker实现WebSocket共享


前言

最近有一个需求,需要实现用户系统消息时时提醒功能。第一时间就是想用WebSocket进行长连接。但是前端项目点击跳转需要打开新的标签页。这个时间就会出现新的标签页打开会把老的WebSocket连接挤掉。然后就想到了去共享一个WebSocket连接。就能实现多个标签页消息共享了。

一、SharedWorker 是什么

SharedWorker 是什么

SharedWorker 是一种特殊类型的 Worker,可以被多个浏览上下文访问,比如多个 windows,iframes 和 workers,但这些浏览上下文必须同源。它们实现于一个不同于普通 worker 的接口,具有不同的全局作用域:SharedWorkerGlobalScope ,但是继承自WorkerGlobalScope

SharedWorker 的使用方式

SharedWorker 线程的创建和使用跟 worker 类似,事件和方法也基本一样。 不同点在于,主线程与 SharedWorker 线程是通过MessagePort建立起链接,数据通讯方法都挂载在SharedWorker.port上。

值得注意的是,如果你采用 addEventListener 来接收 message 事件,那么在主线程初始化SharedWorker()后,还要调用 SharedWorker.port.start() 方法来手动开启端口。

// main.js(主线程)
const myWorker = new SharedWorker('./sharedWorker.js');myWorker.port.start(); // 开启端口myWorker.port.addEventListener('message', msg => {console.log(msg.data);
})

但是,如果采用 onmessage 方法,则默认开启端口,不需要再手动调用SharedWorker.port.start()方法

// main.js(主线程)
const myWorker = new SharedWorker('./sharedWorker.js');myWorker.port.onmessage = msg => {console.log(msg.data);
};

SharedWorker 标识与独占

共享工作者线程标识源自解析后的脚本 URL、工作者线程名称和文档源。(可以通过第二参数给SharedWorker 命名

实例化一个共享工作者线程 
如果你的服务地址正好就是xxx.com那么这三种解析方式就是同一个线程,只会创建一个,类似同源策略
另外两个会在其原有线程上增加一个端口port(需要我们通过创建一个ports数组存起来,方便之后数据分发)
- 全部基于同源调用构造函数
- 所有脚本解析为相同的 URL 
- 所有线程都有相同的名称
new SharedWorker('./sharedWorker.js'); 
new SharedWorker('sharedWorker.js'); 
new SharedWorker('https://xxx.com/sharedWorker.js');

如果当其中URL、工作者线程名称和文档源变更时候都会创建新的线程。

  • 改变url这个好理解
  • 改变文档源
demo中我又创建了一个page3.html
和另一个SharedWorker2.js
// 创建
page3与page1中唯一不同的就是引用了SharedWorker2.js
const worker = new SharedWorker("./SharedWorker2.js");

在这里插入图片描述

改变名字

demo中我又创建了一个page4.html
// 创建
page4和page2中唯一不同的就是给了不同的第2个名字(两种写法,效果相同,只不过对象还能传递其他参数)
page2中(直接给字符串)const worker = new SharedWorker("./SharedWorker.js",'page2');
page4中(给了对象)const worker = new SharedWorker("./SharedWorker.js",{name:'page4'});

在这里插入图片描述

二、Demo使用

demo演示:
在这里插入图片描述
demo条件

  • 需要服务器环境运行。我这边使用的是vs code 插件Live Server(这玩意咋用自己百度下)可以看一下视频里面的地址是127开头的。
  • chrome浏览器(这个不用多说了)要提一点的是SharedWorker 文件里面的console和debugger是不会出现page1 和page2的控制台的,这个需要去专门看线程的地方查看。chrome浏览器通过chrome://inspect/#workers进入。看图:
  • 在这里插入图片描述

上代码
SharedWorker.js

// 记个数
let count = 0;
// 把每个连接的端口存下来
const ports = [];// 连接函数 每次创建都会调用这个函数
onconnect = (e) => {console.log("这里是共享线程展示位置");// 获取端口const port = e.ports[0];// 把丫存起来ports.push(port);// 监听方法port.onmessage = (msg) => {// 这边的console.log是看不到的 debugger也是看不到的 需要在线程里面看console.log("共享线程接收到信息:", msg.data, count);if (msg.data === "+") {count++;}// 循环向所有端口广播ports.forEach((p) => {p.postMessage(count);});};
};

page1.html

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title>SharedWorker-page1</title></head><body><h1>SharedWorker-page1</h1><button id="btn">count++</button><script>const btn = document.querySelector("#btn");// 兼容性判断if (!SharedWorker) {throw new Error("当前浏览器不支持SharedWorker");}// 创建const worker = new SharedWorker("./SharedWorker.js");// 启动worker.port.start();// 线程监听消息worker.port.onmessage = (e) => {console.log("page1共享线程计数值:", e.data);};btn.addEventListener("click", (_) => {worker.port.postMessage("+");});</script></body>
</html>

page2.hrml

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title>SharedWorker-page2</title></head><body><h1>SharedWorker-page2</h1><button id="btn">count++</button><script>const btn = document.querySelector("#btn");// 兼容性判断if (!SharedWorker) {throw new Error("当前浏览器不支持SharedWorker");}// 创建const worker = new SharedWorker("./SharedWorker.js");// 启动worker.port.start();// 线程监听消息worker.port.onmessage = (e) => {console.log("page2共享线程计数值:", e.data);};btn.addEventListener("click", (_) => {worker.port.postMessage("+");});</script></body>
</html>

上面的代码基本上就已经算是OK了。

三、使用SharedWorker实现WebSocket共享

SharedWorker.js
SharedWorker的js文件是需要让各个浏览器页签引用的。所以将文件放在了public中

// 记个数
let count = 0;
// 把每个连接的端口存下来
const ports = [];
var state = {webSocket: null, // webSocket实例lockReconnect: false, // 重连锁,避免多次重连maxReconnect: 6, // 最大重连次数, -1 标识无限重连reconnectTime: 0, // 重连尝试次数heartbeat: {interval: 30 * 1000, // 心跳间隔时间timeout: 10 * 1000, // 响应超时时间pingTimeoutObj: null, // 延时发送心跳的定时器pongTimeoutObj: null, // 接收心跳响应的定时器pingMessage: JSON.stringify({type: 'ping'}), // 心跳请求信息},token:null
}// 连接函数 每次创建都会调用这个函数
onconnect = (e) => {console.log("这里是共享线程展示位置", e);// 获取端口const port = e.ports[0];// 把丫存起来ports.push(port);// 监听方法port.onmessage = (msg) => {// 这边的console.log是看不到的 debugger也是看不到的 需要在线程里面看console.log("共享线程接收到信息:", msg);var data = msg.data || {}var conf = JSON.parse(data)console.log("解析后的参数", conf)switch (conf.type) {case "open":console.log("共享线程状态为Open")if (!state.webSocket) {state.token=conf.tokeninitWebSocket(conf.host, conf.baseURL, conf.uri, state.token, conf.tenant);}breakcase 'portClose':console.log("共享线程状态为portClose")// 关闭当前端口(new SharedWorker 会默认开启端口)if (ports.indexOf(port) > -1) {ports.splice(ports.indexOf(port), 1)}breakcase 'wsClose':// 关闭websocketconsole.log("共享线程状态为WsClose")state.webSocket.close();clearTimeoutObj(state.heartbeat);state.websocket = nullstate.token=nullbreakcase 'close':// 关闭SharedWorker 通过self调用 SharedWorkerGlobalScope 的实例console.log("共享线程状态为close")self.close()breakdefault:break}};
};const initWebSocket = (host, baseURL, uri, token, tenant) => {// ws地址let wsUri = `ws://${host}${baseURL}${uri}?access_token=${token}&TENANT-ID=${tenant}`;// let wsUri = `ws://${host}${baseURL}${other.adaptationUrl(props.uri)}?access_token=${token.value}&TENANT-ID=${tenant.value}`;// let wsUri = `ws://${host}${baseURL}${uri}?access_token=${token}`;// 建立连接state.webSocket = new WebSocket(wsUri);// 连接成功state.webSocket.onopen = onOpen;// 连接错误state.webSocket.onerror = onError;// 接收信息state.webSocket.onmessage = onMessage;// 连接关闭state.webSocket.onclose = onClose;
};const reconnect = () => {if (!state.token) {return;}if (state.lockReconnect || (state.maxReconnect !== -1 && state.reconnectTime > state.maxReconnect)) {return;}state.lockReconnect = true;setTimeout(() => {state.reconnectTime++;// 建立新连接initWebSocket();state.lockReconnect = false;}, 5000);
};
/*** 清空定时器*/
const clearTimeoutObj = (heartbeat) => {heartbeat.pingTimeoutObj && clearTimeout(heartbeat.pingTimeoutObj);heartbeat.pongTimeoutObj && clearTimeout(heartbeat.pongTimeoutObj);
};
/*** 开启心跳*/
const startHeartbeat = () => {const webSocket = state.webSocket;const heartbeat = state.heartbeat;// 清空定时器clearTimeoutObj(heartbeat);// 延时发送下一次心跳heartbeat.pingTimeoutObj = setTimeout(() => {// 如果连接正常if (webSocket.readyState === 1) {//这里发送一个心跳,后端收到后,返回一个心跳消息,webSocket.send(heartbeat.pingMessage);// 心跳发送后,如果服务器超时未响应则断开,如果响应了会被重置心跳定时器heartbeat.pongTimeoutObj = setTimeout(() => {webSocket.close();}, heartbeat.timeout);} else {// 否则重连reconnect();}}, heartbeat.interval);
};/*** 连接成功事件*/
const onOpen = () => {console.log("连接成功")//开启心跳startHeartbeat();state.reconnectTime = 0;
};
/*** 连接失败事件* @param e*/
const onError = () => {console.log("连接 失败")//重连reconnect();
};/*** 连接关闭事件* @param e*/
const onClose = () => {//重连reconnect();
};
/*** 接收服务器推送的信息* @param msgEvent*/
const onMessage = (msgEvent) => {//收到服务器信息,心跳重置并发送console.log("接到消息", msgEvent)startHeartbeat();// const text = JSON.parse(msgEvent.data);ports.forEach((p) => {p.postMessage(msgEvent.data);});
};

定义一个组件叫WebSocket.vue

代码中有一些token的判断可以无视。
我这里怎么简单怎么来。定义一个组件直接放到app.vue中引用(主打的就是一个方便)
我这里接收到消息后使用mitt.js进行各消息分发

<template><div></div>
</template>
<script setup lang="ts" name="global-websocket">
import { Session } from '@/utils/storage';
import {computed, onMounted, onUnmounted, ref,watch} from "vue";
import {eventBus} from "@/utils/eventBus"
import other from "@/utils/other";const props = defineProps({uri: {type: String,},
});
const isLogin=ref<any>()
const worker=ref()
const token = computed(() => {return Session.getToken();
});const tenant = computed(() => {return Session.getTenant();
});
watch(isLogin,(newValue, oldValue) =>{if(newValue){initWebSocket();}
})
onMounted(() => {// initWebSocket();if(sessionStorage.getItem('token')){initWebSocket();}else{window.addEventListener('setItem', () => {isLogin.value = sessionStorage.getItem('token')});}
});onUnmounted(() => {let conf={type:"wsClose",}worker.value.port.postMessage(JSON.stringify(conf))
});const initWebSocket = () => {if (!SharedWorker) {throw new Error("当前浏览器不支持SharedWorker");}
// 创建worker.value = new SharedWorker("../../../public/SharedWorker.js");// 线程监听消息worker.value.port.onmessage = (e:any) => {console.log("接受到消息:", e.data);sendEventBus(JSON.parse(e.data))};let conf={type:"open",host:window.location.host,baseURL:import.meta.env.VITE_API_URL,uri:other.adaptationUrl(props.uri),token:token.value,tenant:tenant.value}worker.value.port.postMessage(JSON.stringify(conf))
};
const sendEventBus=(text:any)=>{switch (text.type){case "pong":return;case "discuss":eventBus.emit('discuss', text);break;case "onlineusers":eventBus.emit('onlineusers', text);break;case "livestart":eventBus.emit('livestart', text);break;case "message_notify":eventBus.emit('message_notify', text);break;}
}
</script>

mitt消息总线的使用

npm install --save mitt

// eventBus.ts
import createEventBus from 'mitt';export const eventBus = createEventBus();

使用

import {eventBus} from "@/utils/eventBus"//发送消息
eventBus.emit('discuss', text);//监听消息
eventBus.on('discuss', (data) => {console.log(data)});

本文借鉴:https://blog.csdn.net/jinke0010/article/details/124248321

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1475062.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

植物神经紊乱小救星来啦!放松小技巧get√

哈喽&#xff0c;小伙伴们&#xff01;今天给大家带来一些超级实用的放松小技巧&#xff0c;特别适合那些时常感到植物神经紊乱&#xff0c;心情紧绷的亲人们哦&#xff01;&#x1f478; &#x1f340;首先&#xff0c;深呼吸大法&#xff01;每次感到紧张或者焦虑的时候&…

小试牛刀-Python生成solana Wallet公私钥

目录 1.编写目的 2.使用依赖 3.实现方法 3.1 Pynacl实现 3.2 ed25519实现 1.编写目的 在使用Python开发solana应用过程中,需要生成solana Wallet公私钥,以实现后续应用操作.这里将Python生成方法进行整理,方便日后的查阅,也能帮助到实现相关功能的朋友。 2.使用依赖 主要…

(十四)向量和矩阵

向量 标量&#xff1a;比如质量/温度/颜色等&#xff0c;没有方向&#xff0c;只有大小的量&#xff0c;称为标量 向量&#xff1a;拥有方向跟大小的物理量/数学量为向量&#xff0c;比如力/速度 向量特性&#xff1a; 1.向量有方向&#xff0c;没有位置 2.向量有大小&#x…

Linux的前世今生

Unix的起源和发展 1969年&#xff0c;AT&T贝尔实验室的Ken Thompson和Dennis Ritchie等人开发了Unix操作系统。Unix的设计理念强调小而简洁的工具&#xff0c;文本流和系统模块化&#xff0c;这些理念后来成为Linux开发的重要基础。1973年&#xff0c;Unix用C语言重新编写…

Angular进阶之九: JS code coverage是如何运作的

环境准备 需要用到的包 node 18.16.0# Javascript 代码编辑"babel/core": "^7.24.7","babel/preset-env": "^7.24.7","babel-loader": "^9.1.3",# 打包时使用的 module&#xff0c; 给代码中注入新的方法# http…

MySQL如何实现数据排序

根据explain的执行计划来看&#xff0c;MySQL可以分为索引排序和filesort 索引排序 如果查询中的order by字句包含的字段已经在索引中&#xff0c;且索引的排列顺序和order by子句一致&#xff0c;则可直接利用索引进行排序&#xff0c;由于索引有序&#xff0c;所以排序效率…

HTML5实现我的音乐网站源码

文章目录 作者&#xff1a;[xcLeigh](https://blog.csdn.net/weixin_43151418) 1.设计来源1.1 界面效果1.2 轮播图界面1.3 音乐播放界面1.4 视频播放界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作…

合合信息大模型“加速器”重磅上线

大模型技术的发展和应用&#xff0c;预示着更加智能化、个性化未来的到来。如果将大模型比喻为正在疾驰的科技列车&#xff0c;语料便是珍贵的“燃料”。本次世界人工智能大会期间&#xff0c;合合信息为大模型打造的“加速器”解决方案备受关注。 在大模型训练的上游阶段&…

AI工具,如何通过 GPT-4o 提高工作效率

文章目录 引言一、理解GPT-4o及其功能二、如何利用GPT-4o提高工作效率1. 代码生成与优化2. 自动化测试与调试3. 技术文档撰写与知识管理 三、实际案例与成功应用1. GitHub 协作与问题解决2. 敏捷开发与迭代优化 四、GPT-4o的挑战与应对策略五、未来展望与发展方向六、结论 &…

软件产品常见推广渠道

软件产品常见推广渠道&#xff0c;文字越少越重要

【机器学习】分类算法-KNN算法实现

一、前言 最近&#xff0c;在学习机器学习相关的内容&#xff0c;就想着能不能跑一些机器学习的Demo,这样更方便后期的学习&#xff0c;于是在B站上&#xff0c;找了一个Up主【abilityjh】的视频&#xff0c;跟着学&#xff0c;跟着敲代码&#xff0c;自己在博客上将学的东西&a…

视频压缩软件哪个压缩最小,视频用什么软件压缩最小

在数字媒体时代&#xff0c;视频内容的生产与分享已成为生活常态。但随之而来的问题就是&#xff0c;大视频文件占用过多存储空间&#xff0c;上传和分享也变得不便。本文将为你揭示如何将视频压缩到最小&#xff0c;同时保持画质清晰。让我们一起探索吧&#xff01; 下载并文件…

ICC2:如何设置route_auto只绕线一轮?

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 星球小伙伴提问&#xff0c;如何设置route_auto只绕线一轮&#xff0c;想看一下short分布。 这个方法分两步: 关掉redundant via优化 set_app_options -name route.common.po…

展厅AI数字人:实现智慧园区与数字孪生的高效交互展示

随着人工智能技术的飞速发展&#xff0c;智慧园区和数字孪生技术已经成为展厅管理和规划的重要工具&#xff0c;展厅AI数字人可以提供沉浸式的展览体验。 展厅大屏幕支持与AI数字人连接&#xff0c;用户可以直接通过语音交互的形式操作大屏幕显示的内容&#xff0c;实现对大屏…

AI工具杂谈

AI是在帮助开发者还是取代他们&#xff1f; 在软件开发领域&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;正在改变开发者的工作方式。无论是代码生成、错误检测还是自动化测试&#xff0c;AI工具正在成为开发者的得力助手。然而&#xff0c;这也引发了对开发者职业…

ROS2 分布式 及 ssh远程控制 和 上传下载文件或文件夹

问题1. 多台计算机连接同一wifi后 &#xff0c;运行ROS2的小乌龟案例&#xff0c;自己的计算机&#xff0c;无法控制其他电脑的小乌龟 按照正常的情况来说&#xff0c;ROS2是DDS的自发现通信机制&#xff0c;只要处在同一wifi网络中&#xff0c; A计算机执行启动小乌龟的命…

下载安装JavaFX及解决报错:缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序|Eclipse

目录 1.下载并解压 2.Eclipse配置 3.报错问题 解决方法1&#xff1a;将javaSE更改到9以下 解决方法2&#xff1a; 使用module-info.java配置解决 1.下载并解压 JavaFX下载地址&#xff1a;JavaFX - Gluon 选择合适自己电脑配置的sdk版本下载 打不开网页的参考这个博客&…

系统架构设计师——计算机体系结构

分值占比3-4分 计算机硬件组成 计算机硬件组成主要包括主机、存储器和输入/输出设备。 主机&#xff1a;主机是计算机的核心部分&#xff0c;包括运算器、控制器、主存等组件。运算器负责执行算术和逻辑运算&#xff1b;控制器负责协调和控制计算机的各个部件&#xff1b;主存…

从零开始的python学习生活1

python函数的对返回值 本来多个return是不行的 这种语法就能接受多个返回值 def hanshu():return 1,"hello",True x,y,z hanshu() print(x) print(y) print(z)函数的多种传参方式 提前说明白了顺序就无所谓了 关键字传递一个传递参数&#xff0c;一个传递键值…

maven编码报错

maven 编译的时候编码报错&#xff1a; classworlds For input string: "ㄻ孛孛"报错原因&#xff1a; maven 编码使用的是UTF-16 &#xff0c;系统中使用UFT-8 解决办法&#xff1a; 如下设置为UTF-8