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

小菜鸟码住 2024-07-08 10:33:02 阅读 91

1.配置连接

websocket.ts文件如下

<code>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">code>

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>`code>

});

}

// 当页面关闭的时候,去销毁这个事务线程 ---> 解决mitt多次触发

// mittBus.all.delete(EventKeyEnum.MSG_COMMON);

});

</script>

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

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

引用到的 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 文件引入

在这里插入图片描述

动态路由也要关闭

在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。