WebSocket 前端使用vue3+ts+elementplus 实现连接

1.配置连接
websocket.ts文件如下

import { ElMessage } from "element-plus";

interface WebSocketProps {
  url: string; // websocket地址
  heartTime?: number; // 心跳时间间隔,默认为 50000 ms
  heartMsg?: string; // 心跳信息,默认为'ping'
  reconnectCount?: number; // 重连次数,默认为 5
  reconnectTime?: number; // 重连时间间隔,默认为 10000 ms
  message: (ev: MessageEvent) => any; // 接收消息的回调
  open?: (ev: Event) => any; // 连接成功的回调
  close?: (ev: CloseEvent) => any; // 关闭的回调
  error?: (ev: Event) => any; // 错误的回调
}

// webSocket 对象
let webSocket: WebSocket | null = null;
// webSocket定时器id
let setIntervalId: NodeJS.Timeout | null = null;

export const initWebSocket = (config: WebSocketProps) => {
  if (typeof WebSocket === "undefined") {
    ElMessage.error("您的浏览器不支持Websocket通信协议,请使用Chrome或者其他高版本的浏览器!");
    return;
  }
  if (webSocket != null && webSocket.readyState === webSocket.OPEN) {
    return webSocket;
  }
  createWebSocket(config);
  return webSocket;
};

/**
 * 创建WebSocket
 * @param config
 */
const createWebSocket = (config: WebSocketProps) => {
  // 初始化 WebSocket
  webSocket = new WebSocket(config.url);
  webSocket.onopen = (ev: Event) => {
    config.open && config.open(ev);
    /**
     * 发送心跳
     * 使用Nginx代理WebSocket的时候,客户端与服务器握手成功后,如果在60秒内没有数据交互,就会自动断开连接。
     * Nginx默认的断开链接时间为60秒
     */
    sendPing(config.heartTime ?? 50000, config.heartMsg ?? "ping");
  };
  webSocket.onmessage = (ev: MessageEvent) => config.message(ev);
  webSocket.onerror = (ev: Event) => error(config, ev);
  webSocket.onclose = (ev: CloseEvent) => close(config, ev);
};

/**
 * 发送心跳
 * @param {number} heartTime 心跳间隔毫秒 默认50000
 * @param {string} heartMsg 心跳名称 默认字符串ping
 */
const sendPing = (heartTime: number, heartMsg: string) => {
  webSocket?.send(heartMsg);
  setIntervalId = setInterval(() => {
    webSocket?.send(heartMsg);
  }, heartTime);
};

/**
 * WebSocket 关闭的回调方法
 * @param config
 */
const close = (config: WebSocketProps, ev: CloseEvent) => {
  config.close && config.close(ev);
  clearInterval(Number(setIntervalId));
};

let falg = false;
// 重连次数
let reconnectCount = 0;
// 重连定时器id
let reconnectId: NodeJS.Timeout | null = null;

/**
 * WebSocket 关闭的回调方法
 * @param config
 */
const error = (config: WebSocketProps, ev: Event) => {
  config.error && config.error(ev);
  if (falg) return;
  reconnectId = setInterval(() => {
    falg = true;
    reconnectCount++;
    console.log("正在重新连接,次数:" + reconnectCount);
    let socket = initWebSocket(config);
    if (socket?.readyState === socket?.OPEN) {
      reconnectCount = 0;
      falg = false;
      clearInterval(Number(reconnectId));
    }
    if (reconnectCount >= 5) {
      clearInterval(Number(reconnectId));
    }
  }, config.reconnectTime ?? 10000);
};

2. 创建链接
新建 websocket.vue文件

<template>
  <div></div>
</template>

<script setup lang="ts" name="WebSocket">
import { useUserStore } from "@/stores/modules/user";
import { DEV_WS_URL_HEAD, DEV_WS_URL_TAIL, PRO_WS_URL_HEAD, PRO_WS_URL_TAIL } from "@/api/config/websocketUrl";
import { WebSocketMsg, EventKeyEnum } from "@/api/interface/webSocketMsg/index";
import { initWebSocket } from "@/utils/websocket";
import { ElMessageBox, ElNotification } from "element-plus";
import mittBus from "@/utils/mittBus";
import { LOGIN_URL } from "@/config";//export const LOGIN_URL: string = "/login";这是登录的路径
const userStore = useUserStore();//   userStore.setToken(data.tokenValue);登录的时候存 token
const router = useRouter();
const webSocket = initWebSocket({
  url:
    (import.meta.env.VITE_WS_FLAG == "production" ? PRO_WS_URL_HEAD + PRO_WS_URL_TAIL : DEV_WS_URL_HEAD + DEV_WS_URL_TAIL) +
    "/webSocketService/" +
    userStore.token,
  open: () => {
    console.info("连接WebSocket成功");
  },
  message: (event: MessageEvent) => {
    const webSocketMsg: WebSocketMsg = JSON.parse(event.data);
    console.log("[webSocketMsg] data: " + event.data);
    switch (webSocketMsg.eventKey) {
      case EventKeyEnum.CONNECTION_SUCCESS:
        mittBus.emit("init_seat");
        break;
      case EventKeyEnum.MSG_COMMON:
        mittBus.emit(EventKeyEnum.MSG_COMMON, event.data);
        break;
      case EventKeyEnum.SATOKEN:
        mittBus.emit(EventKeyEnum.SATOKEN, event.data);
        break;
    }
  },
  close: () => {
    console.log("close");
  },
  error: () => {
    console.log("error");
  }
});

userStore.setWebSocket(webSocket ?? null);
// 后端推送消息,执行相关操作
mittBus.on(EventKeyEnum.SATOKEN, (val: any) => {
  let msgData = JSON.parse(val);
  let eventKey = msgData.eventKey;
  let msgContent = msgData.msgContent;
  // 清除 Token
  userStore.setToken("");
  // 清除用户信息
  userStore.setUserInfo("");
  // 清除所有数据
  userStore?.webSocket?.close();
  userStore.setWebSocket(null);
  // 3.重定向到登陆页
  router.replace(LOGIN_URL);
  if (eventKey == "SATOKEN") {
    ElMessageBox.confirm(msgContent, "提示", {
      confirmButtonText: "确认",
      type: "error",
      showCancelButton: false
    });
  }
  // 当页面关闭的时候,去销毁这个事务线程 ---> 解决mitt多次触发
  mittBus.all.delete(EventKeyEnum.SATOKEN);
});
mittBus.on(EventKeyEnum.MSG_COMMON, (val: any) => {
  let msgData = JSON.parse(val);
  let eventKey = msgData.eventKey;
  let msgContent = msgData.msgContent;
  let sendTime = msgData.sendTime;
  if (eventKey == "MSG_COMMON") {
    ElNotification({
      title: "管理员消息",
      dangerouslyUseHTMLString: true,
      position: "bottom-right",
      duration: 0,
      customClass: "msg",
      message: `<span style="color:gray">${sendTime}<span><br/><pre>${msgContent}</pre>`
    });
  }
  // 当页面关闭的时候,去销毁这个事务线程 ---> 解决mitt多次触发
  // mittBus.all.delete(EventKeyEnum.MSG_COMMON);
});
</script>

<style scoped lang="scss"></style>

下面的文件都是上面第二步用到的文件
引用到的 user 文件

import { defineStore } from "pinia";
import { UserState } from "@/stores/interface";
//UserState用到的类型如下
//export interface UserState {
  token: string;
  tokenName: string;
  userInfo: any;
  webSocket: WebSocket | null;
}

import piniaPersistConfig from "@/stores/helper/persist";

export const useUserStore = defineStore({
  id: "geeker-user",
  state: (): UserState => ({
    token: "",
    tokenName: "",
    userInfo: "",
    webSocket: null
  }),
  getters: {},
  actions: {
    // Set Token
    setToken(token: string) {
      this.token = token;
    },
    setTokenName(tokenName: string) {
      this.tokenName = tokenName;
    },
    // Set setUserInfo
    setUserInfo(userInfo: any) {
      this.userInfo = userInfo;
    },
    // setWebSocket
    setWebSocket(webSocket: WebSocket | null) {
      this.webSocket = webSocket;
    }
  },
  persist: piniaPersistConfig("geeker-user")
});

持久化文件 pinia
persist.ts

import { PersistedStateOptions } from "pinia-plugin-persistedstate";

/**
 * @description pinia 持久化参数配置
 * @param {String} key 存储到持久化的 name
 * @param {Array} paths 需要持久化的 state name
 * @return persist
 * */
const piniaPersistConfig = (key: string, paths?: string[]) => {
  const persist: PersistedStateOptions = {
    key,
    storage: localStorage,
    // storage: sessionStorage,
    paths
  };
  return persist;
};

export default piniaPersistConfig;

websocketUrl文件
websocketUrl.ts

/**
 * 连接WebSocket服务地址的网关IP端口 -- 开发环境
 * (解决扫描漏洞:IP地址泄露)
 */

// 头部
//示例"ws://199.166.0."
export const DEV_WS_URL_HEAD = "";

// 尾部
//示例"11:1111"
export const DEV_WS_URL_TAIL = "";

/**
 * 连接WebSocket服务地址的网关IP端口 -- 正式环境
 * (解决扫描漏洞:IP地址泄露)
 */

// 头部
//示例"ws://00.111."
export const PRO_WS_URL_HEAD = "";

// 尾部
//示例"111.11:1111"
export const PRO_WS_URL_TAIL = "";

@/api/interface/webSocketMsg/index.ts 文件如下

/**
 * WebSocket 消息类型
 */
export interface WebSocketMsg {
  /**
   * 事件标识
   **/
  eventKey: EventKeyEnum | "";

  /**
   * 用户id
   **/
  userId: string;

  /**
   * 用户所属团队id
   **/
  userTeamId?: string;

  /**
   * 用户token
   **/
  token?: string;
  /**
   * 消息内容
   *
   **/
  msgContent: string;

  /**
   * 消息发送时间(yyyy-MM-dd HH:mm:ss)
   *
   **/
  sendTime: string;
  /**
   * 是否发送给所有人
   *
   **/
  everyone: boolean;
}

export enum EventKeyEnum {
  /**
   * WebSocket连接成功标识,根据后台定义
   */
  CONNECTION_SUCCESS = "",
  /**
   * 提醒消息推送
   */
  MSG_COMMON = "",

  /**
   * 用户登录认证相关消息
   */
  SATOKEN = ""
}

mitt 使用
mittBus.ts文件

import mitt from "mitt";

const mittBus = mitt();

export default mittBus;

番外
在响应拦截器要关闭连接
在这里插入图片描述
在这里插入图片描述
退出登录也关闭连接
在这里插入图片描述
在框架main 文件引入
在这里插入图片描述
动态路由也要关闭
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

利用 NRF24L01 无线收发模块实现传感器数据的无线传输

NRF24L01 是一款常用的无线收发模块&#xff0c;适用于远程控制和数据传输应用。本文将介绍如何利用 NRF24L01 模块实现传感器数据的无线传输&#xff0c;包括硬件的连接和配置&#xff0c;以及相应的代码示例。 一、引言 NRF24L01 是一款基于 2.4GHz 射频通信的低功耗无线收发…

PG14归档失败解决办法archiver failed on wal_lsn

案例1:pg_wal下有wal_lsn文件 案例1适用于以下场景&#xff1a; pg_wal下有该wal_lsn文件而归档目录下无该wal_lsn文件pg_wal和归档目录下同时都有该wal_lsn文件 问题描述 昨晚RepmgrPG14主备主库因wal日志撑爆磁盘&#xff0c;删除主库过期wal文件重做备库后上午进行主备状…

Find My文件袋|苹果Find My技术与文件袋结合,智能防丢,全球定位

文件袋是指用于对自己的私人物品或身份文件保管等、资料袋、白卷宗。款式分为扣式和文件套式。文件袋通常具有足够的容量&#xff0c;可以容纳大量文件。当需要长期存档文件时&#xff0c;文件袋是一个方便的选择&#xff0c;可以将文件整理好放入文件袋中&#xff0c;便于存放…

[github全教程]github版本控制最全教学------- 大厂找工作面试必备!

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于新西兰奥克兰大学攻读IT硕士学位。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。跨领域…

【笔记】2023最新Python安装教程(Windows 11)

&#x1f388;欢迎加群交流&#xff08;备注&#xff1a;csdn&#xff09;&#x1f388; ✨✨✨https://ling71.cn/hmf.jpg✨✨✨ &#x1f913;前言 作为一名经验丰富的CV工程师&#xff0c;今天我将带大家在全新的Windows 11系统上安装Python。无论你是编程新手还是老手&…

C语言错误处理之 “<errno.h>与<error.h>”

目录 前言 错误号处理方式 errno.h头文件 error.h头文件 参数解释&#xff1a; 关于的”__attribute__“解释&#xff1a; 关于“属性”的解释&#xff1a; 实例一&#xff1a; 实例二&#xff1a; error.h与errno.h的区别 补充内容&#xff1a; 前言 在开始学习…

Redis——某马点评day01——短信登录

项目介绍 导入黑马点评项目 项目架构 基于Session实现登录 基本流程 实现发送短信验证码功能 controller层中 /*** 发送手机验证码*/PostMapping("code")public Result sendCode(RequestParam("phone") String phone, HttpSession session) {// 发送短信…

支持Upsert、Kafka Connector、集成Airbyte,Milvus助力高效数据流处理

Milvus 已支持 Upsert、 Kafka Connector、Airbyte&#xff01; 在上周的文章中《登陆 Azure、发布新版本……Zilliz 昨夜今晨发生了什么&#xff1f;》&#xff0c;我们已经透露过 Milvus&#xff08;Zilliz Cloud&#xff09;为提高数据流处理效率&#xff0c; 先后支持了 Up…

JOSEF约瑟 DY-34 型电压继电器,15-30V 柜内安装,板前接线

DY-30系列电压继电器 DY-32电压继电器&#xff1b; DY-36电压继电器&#xff1b; DY-33电压继电器&#xff1b; DY-37电压继电器&#xff1b; DY-34电压继电器&#xff1b; DY-38电压继电器&#xff1b; DY-31电压继电器&#xff1b; DY-35电压继电器&#xff1b; DY-32/60C电压…

HarmonyOS——解决本地模拟器无法选择设备的问题

在使用deveco studio进行鸿蒙开发的时候&#xff0c;可能会遇到本地模拟器已经启动了&#xff0c;但是仍然无法选择本地模拟器中的设备&#xff0c;尤其在MAC环境中尤为常见。 解决办法&#xff1a; 先打开IDE启动本地模拟器&#xff0c;等模拟器启动后&#xff0c;退出IDE重新…

蓝桥杯每日一题2023.12.1

题目描述 蓝桥杯大赛历届真题 - C 语言 B 组 - 蓝桥云课 (lanqiao.cn) 题目分析 对于此题目而言思路较为重要&#xff0c;实际可以转化为求两个数字对应的操作&#xff0c;输出最前面的数字即可 #include<bits/stdc.h> using namespace std; int main() {for(int i 1…

ARM64版本的chrome浏览器安装

这一快比较玄学&#xff0c;花个半个小时左右才能安装好&#xff0c;也不知道是个什么情况。 sudo snap install chromium只需要以上这个命令&#xff0c;当然&#xff0c;也可以自己去找安装包进行安装&#xff0c;但是测试后发现并没有那么好装&#xff0c;主要是两个部分 一…

第九节HarmonyOS 常用基础组件-Text

一、组件介绍 组件&#xff08;Component&#xff09;是界面搭建与显示的最小单位&#xff0c;HarmonyOS ArkUI声名式为开发者提供了丰富多样的UI组件&#xff0c;我们可以使用这些组件轻松的编写出更加丰富、漂亮的界面。 组件根据功能可以分为以下五大类&#xff1a;基础组件…

Unity中Shader指令优化

文章目录 前言一、解析一下不同运算所需的指令数1、常数基本运算2、变量基本运算3、条件语句、循环 和 函数 前言 上一篇文章中&#xff0c;我们解析了Shader解析后的代码。我们在这篇文章中来看怎么实现Shader指令优化 Unity中Shader指令优化&#xff08;编译后指令解析&…

C语言之联合和枚举

C语言之联合和枚举 文章目录 C语言之联合和枚举1. 联合体1.1 联合体的声明1.2 联合体的特点1.3 结构体和联合体对比1.4 联合体大小的计算1.5 联合体小练习 2. 枚举2.1 枚举类型的声明2.2 枚举类型的优点2.3 枚举类型的使用 1. 联合体 1.1 联合体的声明 像结构体⼀样&#xff…

AI PC行业深度报告:格局演变、发展趋势、产业链及相关公司深度梳理

今天分享的是AI PC系列深度研究报告&#xff1a;《AI PC行业深度报告&#xff1a;格局演变、发展趋势、产业链及相关公司深度梳理》。 &#xff08;报告出品方&#xff1a;慧博智能投研&#xff09; 报告共计&#xff1a;21页 一、AI PC的产生 1、端侧 AI 是 AI 发展下一阶段…

【ASP.NET CORE】数据迁移 codefirst

已经写好实体类&#xff0c;使用add-migration生成数据迁移语句&#xff0c;注意如果项目中有多个dbcontext需要使用 -context 名称&#xff0c;指定下需要使用的dbcontext add-Migration Address -context mvcsqlcontext运行后会生成两个文件 2. 使用Update-Database语句更…

Postman如何导入和导出接口文件

本文介绍2种导出和导入的操作方法&#xff1a;一种是分享链接&#xff0c;导入链接的方式&#xff08;需要登录&#xff09;&#xff1b;另一种是导出json文件&#xff0c;再次导入。下面将详细介绍。 由于第一种分享链接&#xff0c;导入链接的方式需要登录&#xff0c;所以推…

Nacos 架构原理

基本架构及概念​ 服务 (Service)​ 服务是指一个或一组软件功能&#xff08;例如特定信息的检索或一组操作的执行&#xff09;&#xff0c;其目的是不同的客户端可以为不同的目的重用&#xff08;例如通过跨进程的网络调用&#xff09;。Nacos 支持主流的服务生态&#xff0c…

【每日一题】找出叠涂元素

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希表 写在最后 Tag 【哈希表】【数组】【2023-12-01】 题目来源 2661. 找出叠涂元素 题目解读 从左往右遍历 arr 给矩阵 mat 上色&#xff0c;在上色的过程中矩阵的某一行或者某一列的全部被上色了&#xff0c;返回…