Vue 3 客户端 WebSocket 封装:自动重连、心跳与全局状态同步
test12026-10-2725 次阅读
为什么需要一层封装
原生 WebSocket 在弱网、刷新、后台切回前台时经常断开,业务里直接 new WebSocket 会导致到处散落重连逻辑。把连接、心跳、重连、消息分发统一抽到 composable,组件只关心"收到什么数据、要发什么"。
核心 composable
// useWebSocket.ts
import { ref, onUnmounted } from "vue";
export function useWebSocket(url: string) {
const socket = ref<WebSocket | null>(null);
const connected = ref(false);
const messages = ref<any[]>([]);
let heartbeat: number | undefined;
let retry = 0;
function connect() {
const ws = new WebSocket(url);
socket.value = ws;
ws.onopen = () => {
connected.value = true;
retry = 0;
heartbeat = window.setInterval(
() => ws.send(JSON.stringify({ type: "ping" })),
15000
);
};
ws.onmessage = (e) => {
const data = JSON.parse(e.data);
if (data.type !== "pong") messages.value.push(data);
};
ws.onclose = () => {
connected.value = false;
window.clearInterval(heartbeat);
if (retry < 5) {
retry++;
setTimeout(connect, Math.min(1000 * retry, 5000));
}
};
}
function send(payload: unknown) {
if (socket.value?.readyState === WebSocket.OPEN) {
socket.value.send(JSON.stringify(payload));
}
}
connect();
onUnmounted(() => {
window.clearInterval(heartbeat);
socket.value?.close();
});
return { connected, messages, send };
}
在组件里消费
// ChatRoom.vue
<script setup lang="ts">
import { useWebSocket } from "./useWebSocket";
const { connected, messages, send } = useWebSocket("wss://api.example.com/ws");
function say(text: string) {
send({ type: "chat", text });
}
</script>
工程注意点
- 心跳间隔要小于服务端超时阈值,一般 15-30 秒一次 ping。
- 重连用指数退避,避免服务端恢复瞬间被大量连接打爆。
- 组件卸载时一定清掉定时器并关闭连接,否则会泄漏 socket。
- 跨标签页共享一份连接时,把 composable 提到应用根部或用 provide/inject 复用单例。
T
test1
文章作者
为什么需要一层封装 原生 WebSocket 在弱网、刷新、后台切回前台时经常断开,业务里直接 new WebSock...
- 分类
- 技术
- 发布时间
- 2026-10-27
- 字数
- 约 1785 字
- 阅读
- 25 次
