WebSocket 前端使用vue3+ts+elementplus 实现连接
时间:2024-04-12 20:50:19 来源:网络cs 作者:纳雷武 栏目:卖家故事 阅读:
阅读本书更多章节>>>>
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 文件引入
动态路由也要关闭
本文链接:https://www.kjpai.cn/gushi/2024-04-12/157795.html,文章来源:网络cs,作者:纳雷武,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。