前言

最近在做一个 H5 仿新版 QQ 的一个项目,使用的技术栈为 Vue3 + TypeScript + Vite + Pinia + Naive UI

其中最主要的核心功能就是 WebSocket,建立长连接,实现即时通信效果

正文

首先在src/utils下新建一个 websocket.ts 文件,里面是封装的 websocket 类(其中部分功能需要根据自己业务需求修改)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
class WebSocketClass {
private wsurl: string;
private id: number;
private socketTask: WebSocket | null = null;
private isConnected: boolean = false; //是否关闭连接
private heartbeatIntervalId: ReturnType<typeof setInterval> | null = null;
private reconnectTimeoutId: ReturnType<typeof setTimeout> | null = null;

constructor(
wsurl: string,
id: number,
private heartbeatInterval: number = 3 //心跳间隔
) {
this.wsurl = wsurl; // 连接地址
this.id = id; // 用户id
this.connect(); // 立即尝试连接
}

private connect(): void {
this.socketTask = new WebSocket(this.wsurl);

this.socketTask.onopen = () => {
console.log("WebSocket连接成功!");

this.isConnected = true;
this.startHeartbeat();
};

this.socketTask.onmessage = (messageEvent) => {
//写自己收到消息的逻辑
console.log("接收到WebSocket消息:", JSON.parse(messageEvent.data));
const newMessage = JSON.parse(messageEvent.data);
...
};

this.socketTask.onerror = () => {
console.error("WebSocket连接错误!");
this.isConnected = false;
};

this.socketTask.onclose = () => {
console.log("WebSocket连接已关闭!");
//判断是否登录逻辑,如果退出登录,断开连接,否则进行重连
const isLogin = JSON.parse(localStorage.getItem("user") as string).isLogin;
this.isConnected = false;
if (isLogin) {
this.tryReconnect();
}
};
}

//心跳检测
private startHeartbeat(): void {
if (this.heartbeatIntervalId) clearInterval(this.heartbeatIntervalId); // 清理旧的心跳
this.heartbeatIntervalId = setInterval(() => {
if (this.socketTask && this.isConnected) {
//心跳检测消息(看后端需求)
const heartbeatMessage = {
Action: "1",
Sender: this.id,
};
this.socketTask.send(JSON.stringify(heartbeatMessage));
console.log("发送心跳检测消息:", heartbeatMessage);
}
}, this.heartbeatInterval * 1000);
}

//心跳重连
private tryReconnect(): void {
if (this.reconnectTimeoutId) clearTimeout(this.reconnectTimeoutId); // 清理旧的重连
this.reconnectTimeoutId = setTimeout(() => {
console.log("尝试重新连接WebSocket...");
this.connect();
}, 5000);
}

//发送消息
send(message: string): void {
if (this.socketTask && this.isConnected) {
this.socketTask.send(message);
} else {
console.error("发送失败:WebSocket未连接或已关闭。");
}
}

//关闭连接
close(): void {
if (this.heartbeatIntervalId) clearInterval(this.heartbeatIntervalId); // 停止心跳
if (this.reconnectTimeoutId) clearTimeout(this.reconnectTimeoutId); // 阻止重连

if (this.socketTask && this.isConnected) {
this.socketTask.close();
this.socketTask = null;
}

this.isConnected = false;
}
}

export default WebSocketClass;

定义接口

1
2
3
4
export interface IWebSocket {
send(message: string): void;
close(): void;
}

如何使用

在需要使用的组件中,进行引入,初始化实例

1
2
3
4
5
6
7
8
import type { IWebSocket } from "@/model/xxx";
import websocket from "@/utils/websocket.ts";

//建立ws通信
const ws: IWebSocket = new websocket(
`ws://xxxx/ws?id=${userInfo.uid}`,
userInfo.uid
) as IWebSocket;

此时已经建立连接

会自动进行心跳检测

需要进行通信时,调用send()发送消息