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

news2024/11/17 19:46:28

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 文件引入
在这里插入图片描述
动态路由也要关闭
在这里插入图片描述

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

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

相关文章

Edge 旧版本回退

微软官网 下载策略文件 下载后&#xff0c;解压打开 cad 包&#xff0c;把里面的 Windows\ADMX\ 下 3 个 *.admx 文件解压到 C:\Windows\PolicyDefinitions Windows\ADMX\zh-CN 下 3 个 *.adlm 文件解压到 C:\Windows\PolicyDefinitions\zh-CN Windows 搜索 gpedit&#xff…

水果软件FL Studio最新21.1.1.3750汉化版

FL Studio水果音乐编曲软件中文版,一款强大的音乐制作软件,可以进行音乐编曲、剪辑、录音、混音。FL Studio21.1.1.3750中文版是数字音频工作站 (DAW) 之一&#xff0c;日新月异。它是一款录音机和编辑器&#xff0c;可让您不惜一切代价制作精美的音乐作品并保存精彩的活动画廊…

分布式ID生成框架Leaf升级踩坑

背景&#xff1a; 在项目中需要一个统一的拿单号等唯一ID的服务&#xff0c;就想起了之前用到的leaf&#xff0c;但是因为项目要求&#xff0c;leaf的版本不符合&#xff0c;需要做一些升级 项目地址&#xff1a;https://github.com/Meituan-Dianping/Leaf 升级点&#xff1…

LangChain的函数,工具和代理(三):LangChain中轻松实现OpenAI函数调用

在我之前写的两篇博客中:OpenAI的函数调用,LangChain的表达式语言(LCEL)中介绍了如何利用openai的api来实现函数调用功能&#xff0c;以及在langchain中如何实现openai的函数调用功能&#xff0c;在这两篇博客中&#xff0c;我们都需要手动去创建一个结构比较复杂的函数描述变量…

【前端】-【electron】

文章目录 介绍electron工作流程环境搭建 electron生命周期&#xff08;app的生命周期&#xff09;窗口尺寸窗口标题自定义窗口的实现阻止窗口关闭父子及模态窗口自定义菜单 介绍 electron技术架构&#xff1a;chromium、node.js、native.apis electron工作流程 桌面应用就是…

SQL Server 2016(为数据表Porducts添加数据)

1、实验环境。 某公司有一台已经安装了SQL Server 2016的服务器&#xff0c;并已经创建了数据库PM。 2、需求描述。 在数据库PM中创建表products&#xff0c;"编号"列的值自动增长并为主键。然后使用T-SQL语句为表格插入如下数据。 3、实验步骤。 1、使用SSMS管理工…

2022年高校大数据挑战赛B题图像信息隐藏求解全过程论文及程序

2022年高校大数据挑战赛 B题 图像信息隐藏 原题再现&#xff1a; 互联网的快速发展&#xff0c;给图像、视频的传播方式带来巨大变化。图像作为媒体的重要载体&#xff0c;每天有大量的原创图像公开在互联网上&#xff0c;如何保护图像版权的同时不破坏原始的图像一直是图像处…

反转链表的实现

题目描述&#xff1a; 给出一个链表的头节点&#xff0c;将其反转&#xff0c;并返回新的头节点 思路1&#xff1a;反转地址 将每个节点里的地址由指向下一个节点变为指向前一个节点 定义三个结构体指针n1,n2,n3,n1表示改后指针的地址&#xff0c;n2表示要修改结构体里next的…

手机上的记事本怎么打开?安卓手机通用的记事本APP

有不少上班族发现&#xff0c;自己想要在电脑上随手记录一些工作文字内容&#xff0c;直接使用电脑上的记事本工具来编辑文字是比较便捷的。但是如果想要在手机上记录文字内容&#xff0c;就找不到手机上的记事本了。那么手机上的记事本怎么打开&#xff1f;安卓手机通用的记事…

AWS Remote Control ( Wi-Fi ) on i.MX RT1060 EVK - 1 “建立开发环境”

这个系列的文章将叙述如何借由 NXP 的“evkmimxrt1060_aws_remote_control_wifi_nxp”这支 Sample Code&#xff0c;达到 NXP RT1060EVK 经由 U-Blox EVK-JODY-W263 将资讯传到 AWS 上&#xff0c;并可借由手机对 RT1060 EVK 的 LED 进行远端控制。 整体架构如下图所示&#x…

VUE语法-(readonly的用法)将数据设置成只读模式

1、功能概述 在Vue中定义一个变量&#xff0c;这个变量的值不允许被修改&#xff0c;核心是通过readonly设置成只读。 如果不会使用ref和reactive响应式数据参考如下博客&#xff1a; https://blog.csdn.net/tangshiyilang/article/details/134701103 2、具体实现 如下案例…

41 - 如何使用缓存优化系统性能?

缓存是我们提高系统性能的一项必不可少的技术&#xff0c;无论是前端、还是后端&#xff0c;都应用到了缓存技术。前端使用缓存&#xff0c;可以降低多次请求服务的压力&#xff1b;后端使用缓存&#xff0c;可以降低数据库操作的压力&#xff0c;提升读取数据的性能。 今天我…

LeetCode | 965. 单值二叉树

LeetCode | 965. 单值二叉树 OJ链接 首先判断树为不为空&#xff0c;为空直接true然后判断左子树的val&#xff0c;和根的val相不相同再判断右子树的val&#xff0c;和根的val相不相同最后递归左子树和右子树 bool isUnivalTree(struct TreeNode* root) {if(root NULL)retur…

windows下如何搭建属于自己的git服务器?

windows下如何搭建属于自己的git服务器&#xff1f; 工具准备&#xff08;此章节为网上摘要&#xff0c;忘记出自哪里了&#xff0c;大家自行参考&#xff09;实操步骤 工具准备&#xff08;此章节为网上摘要&#xff0c;忘记出自哪里了&#xff0c;大家自行参考&#xff09; …

c语言详解牛顿迭代法以及求解倒数和平方根

Newtons iteration method 是在实数域和复数域利用切线不断逼近方程根的一种求高次曲线方程的方法&#xff0c;区别于梯度下降法&#xff0c;它是二阶导&#xff0c;收敛速度比较快&#xff0c;对于非凸函数&#xff0c;牛顿法容易受到鞍点或者最大值点的吸引。由于牛顿迭代法是…

[英语学习][3][Word Power Made Easy]的精读与翻译优化

[序言] 这次翻译校验, 难度有点大, 原版中英翻译已出现了严重地偏差. 昨晚11点开始阅读如下段落, 花费了1个小时也没有理解原作者的核心表达, 索性睡觉了. 今早学习完朗文单词之后, 9点半开始继续揣摩. 竟然弄到了中午11点30, 终于明白原作者要表达的意思了. 废话不多说&#x…

笔记----单纯剖分----1

笔记----单纯剖分 定义 线性组合仿射组合&#xff1a; 线性组合的系数为1凸组合&#xff1a; 仿射组合所有的系数都是正数 凸集 R^m 的 任意有限个点的凸组合仍在其中的子集仿射子空间 R^m 的 任意有限个点的仿射组合仍在其中的子集凸包 conv(A) A是R^m的一个子集 A的所有有限凸…

【小布_ORACLE笔记】Part11-6 RMAN Backups

【小布_ORACLE笔记】Part11-6 RMAN Backups 1.track文件的作用 当做差异性备份时&#xff0c;server process对应的RMAN客户端的server process就不用去每个块每个块的检查&#xff0c;只要到trackfile 里面去读一下&#xff0c;看哪个块改变了就直接把哪个块备份下来&#x…

轻量封装WebGPU渲染系统示例<40>- 多层材质的Mask混合(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/MaskTextureEffect.ts 当前示例运行效果: 两层材质效果: 三层材质效果: 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下&#xff1a; export c…

【Go】protobuf介绍及安装

目录 一、Protobuf介绍 1.Protobuf用来做什么 2. Protobuf的序列化与反序列化 3. Protobuf的优点和缺点 4. RPC介绍 <1>文档规范 <2>消息编码 <3>传输协议 <4>传输性能 <5>传输形式 <6>浏览器的支持度 <7>消息的可读性和…