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.mzph.cn/news/870370.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

stm32——AD采集以及DMA

今天继续我们的STM32的内容学习&#xff0c;我使用的单片机是STM32F103VCT6,通过Keil Array Visualization软件来观测AD采样出来的波形。先来看看本次实验用到的硬件知识。 首先是ADC&#xff08;Analog-to-Digital Converter&#xff09;是模拟信号转数字信号的关键组件&#…

x264 编码器 AArch64 汇编函数模块关系分析

x264 编码器 AArch64 汇编介绍 x264 是一个流行的开源视频编码器,它实现了 H.264/MPEG-4 AVC 标准。x264 项目致力于提供一个高性能、高质量的编码器,支持多种平台和架构。对于 AArch64(即 64 位 ARM 架构),x264 编码器利用该架构的特性来优化编码过程。在 x264 编码器中,…

纹波电流与ESR:解析电容器重要参数与应用挑战

电解电容纹波电流与ESR&#xff08;Equivalent Series Resistance&#xff09;是电容器的重要参数&#xff0c;用来描述电容器对交流信号的响应能力和能量损耗。电解电容纹波电流是指电容器在工作时承受的交流信号电流&#xff0c;而ESR则是电容器内部等效电阻&#xff0c;影响…

下载设计免抠元素,就上这6个网站,免费下载!

寻找免费PNG免抠素材网站是创意设计者们探索的重要一环。这些网站提供了丰富的PNG格式素材&#xff0c;去除了背景&#xff0c;方便在不同项目中使用。精心挑选了6个免费PNG免抠素材网站&#xff0c;它们提供了高品质的素材资源&#xff0c;无论是个人设计还是商业项目&#xf…

CVE-2024-23692: Rejetto HTTP File Server 2.3m Unauthenticated RCE漏洞复现

目录 本文章仅供学习使用&#xff01;&#xff01;&#xff01; Rejetto HTTP介绍 漏洞简介 漏洞环境 漏洞复现 exp 复现 结果 如何修复 本文章仅供学习使用&#xff01;&#xff01;&#xff01; Rejetto HTTP介绍 Rejetto是一个流行的开源软件项目&#xff0c;主要…

python开发-创建项目

一、创建项目 1.1在终端 1. 进入某个目录&#xff08;项目放在哪&#xff09; 2. 执行命令创建项目 django-admin startproject 项目名称1.2 在pycharm中创建项目 二、创建app 创建app命令 django-admin startapp app01注册app 编写URL和视图函数对应关系 编写视图函数…

1950年-2021年中国历年民航航线里程统计报告

数据为1950年到2021年我国每年的民航航线总里程数据。 2021年&#xff0c;我国定期航班航线总里程为689.78万公里&#xff0c;相比2019年下降了258.44万公里。 数据统计单位为&#xff1a;公里. 数据说明&#xff1a; 2011年起民航航线里程改为定期航班航线里程 我国定期航班…

怎么将图片批量压缩处理?不牺牲图片清晰度的压缩秘诀

#北京city清凉walk指南# 夏日的北京&#xff0c;满目的绿色和清新空气让人沉醉。 然而&#xff0c;摄影爱好者们在记录这些美好瞬间的同时&#xff0c;也面临着大量图片的存储与管理难题。 随着手机和相机像素的提高&#xff0c;每张照片都可能成为存储空间的"大户&quo…

从0到1开发一个Vue3的新手引导组件(附带遇到的问题以及解决方式)

1. 前言: 新手引导组件,顾名思义,就是强制性的要求第一次使用的用户跟随引导使用应用,可以让一些第一次使用系统的新手快速上手,正好我最近也遇到了这个需求,于是就想着开发一个通用组件拿出来使用(写完之后才发现element就有,后悔了哈哈哈&#x1f62d;&#x1f62d;) 示例图…

【芯片方案】珠宝手机秤方案

珠宝手机秤作为一种便携式电子称重设备&#xff0c;因其小巧、便携、精度高等特点&#xff0c;广泛应用于各种需要精确称重的场景。可能这个目前在国内使用的人比较少&#xff0c;但在西方国家珠宝手机秤却是可以用来送礼的物品。因为珠宝手机秤的外观跟手机外观大多相似&#…

顶顶通呼叫中心中间件-打电话没声音检查步骤(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-电话没声音检查步骤(mod_cti基于FreeSWITH) 检查步骤 1、检查配置文件 检查配置文件&#xff1a;打开ccadmin -> 配置文件 -> vars -> external_ip$${local_ip_v4}看一下这个有没有配置正确的外网IP&#xff0c;如果没有配置正确就需要配置正…

PyCharm 2023.3.2 关闭时一直显示正在关闭项目

文章目录 一、问题描述二、问题原因三、解决方法 一、问题描述 PyCharm 2023.3.2 关闭时一直显示正在关闭项目 二、问题原因 因为PyCharm还没有加载完索引导致的 三、解决方法 方法一&#xff1a; 先使用任务管理器强制关闭&#xff0c;下次关闭时注意要等待PyCharm加载完索…

C语言-顺序表

&#x1f3af;引言 欢迎来到HanLop博客的C语言数据结构初阶系列。在这个系列中&#xff0c;我们将深入探讨各种基本的数据结构和算法&#xff0c;帮助您打下坚实的编程基础。本次我将为你讲解。顺序表&#xff08;也称为数组&#xff09;是一种线性表&#xff0c;因其简单易用…

ArcGIS Pro入门制图教程

地理信息系统 (GIS) 是一种使用地图显示和分析数据的方式。在本教程中&#xff0c;您将学习桌面 GIS 应用程序 ArcGIS Pro 的基础知识。 新加坡的一家旅行社希望制作一款宣传册&#xff0c;用于向游客介绍距离市中心热门目的地最近的火车站。该宣传册将与带有文本信息的地图相…

使用 `useAppConfig` :轻松管理应用配置

title: 使用 useAppConfig &#xff1a;轻松管理应用配置 date: 2024/7/11 updated: 2024/7/11 author: cmdragon excerpt: 摘要&#xff1a;本文介绍了Nuxt开发中useAppConfig的使用&#xff0c;它便于访问和管理应用配置&#xff0c;支持动态加载资源、环境配置切换、权限…

软考:软件设计师 — 2.操作系统

二. 操作系统 1. 操作系统概念 &#xff08;1&#xff09;操作系统的作用 操作系统是计算机硬件之上的第一层软件系统。 操作系统通常用来&#xff1a; 管理系统的硬件、软件、数据资源。控制程序运行。人机之间的接口。应用软件与硬件之间的接口。 可概括为&#xff1a; …

【Linux】内核文件系统系统调用流程摸索

内核层可以看到当前调用文件处理的进程ID 这个数据结构是非常大的&#xff1a; 我们打印的pid,tgid就是从这里来的&#xff0c;然后只需要找到pid_t的数据类型就好了。 下图这是运行的日志信息&#xff1a; 从上述日志&#xff0c;其实我也把write的系统调用加了入口的打印信…

CSS3实现彩色变形爱心动画【附源码】

随着前端技术的发展&#xff0c;CSS3 为我们提供了丰富的动画效果&#xff0c;使得网页设计更加生动和有趣。今天&#xff0c;我们将探讨如何使用 CSS3 实现一个彩色变形爱心加载动画特效。这种动画不仅美观&#xff0c;而且可以应用于各种网页元素&#xff0c;比如加载指示器或…

水库大坝安全监测险情主要内容

水库常见险情主要包括洪水漫顶、脱坡滑坡、坝体裂缝、 散浸、渗漏、漏洞、陷坑、管涌等&#xff0c;此外风浪冲击、水流冲刷等也会加剧险情的扩大。大坝险情万一抢护不及时&#xff0c;易导致发 生溃坝事故&#xff0c;造成极为严重的灾难性后果。要做到及时有效地 抢护大坝险情…

科技信息项目验收测试包括哪些内容?验收测试报告如何获取?

科技信息项目验收测试是指在科技信息项目完成后&#xff0c;组织专业测评团队对项目开发过程和成果进行全面、系统、客观的评测和鉴定的过程。通过验收测试&#xff0c;可以评估项目的质量、功能完整性以及满足业务需求的程度&#xff0c;并为项目的成功上线提供依据。 为了进…