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

前端 0

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定时器idlet 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;// 重连定时器idlet 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);登录的时候存 tokenconst 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 文件引入
在这里插入图片描述
动态路由也要关闭
在这里插入图片描述

也许您对下面的内容还感兴趣: