0


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


本文转载自: https://blog.csdn.net/hfyldx/article/details/134738173
版权归原作者 小菜鸟码住 所有, 如有侵权,请联系我们删除。

“WebSocket 前端使用vue3+ts+elementplus 实现连接”的评论:

还没有评论