基于 Vue + VueUse 的 WebSocket 优雅封装:打造高可用的全局连接管理方案

在现代前端开发中,WebSocket 作为全双工通信协议,被广泛应用于实时消息推送、在线协作、实时数据监控等场景。但原生 WebSocket API 使用繁琐,且在多连接、重连、心跳检测、状态管理等场景下需要大量重复代码。本文将分享基于 Vue3 + VueUse 的useWebSocket钩子,封装一套高可用、可扩展的全局 WebSocket 管理方案,解决上述痛点。

一、封装思路与核心特性

设计目标

  1. 单例管理:全局唯一的 WebSocket 管理器,避免重复创建连接

  2. 自动重连:基于 VueUse 内置能力实现可配置的重连策略

  3. 心跳检测:定时发送心跳包,确保连接活性

  4. 多连接支持:支持同时管理多个不同 URL 的 WebSocket 连接

  5. 事件订阅:灵活的消息、状态、心跳事件订阅机制

  6. 连接限制:防止过多连接占用资源

  7. 优雅的 API 封装:提供简洁、语义化的上层 API,降低使用成本

核心技术栈

  • Vue3(组合式 API)

  • VueUseuseWebSocket(封装了 WebSocket 的核心能力,如重连、心跳)

  • ES6+(Class、Map、解构赋值等)

二、核心代码实现

1. 底层 WebSocket 管理器(websocket-manager.js)

实现全局连接的核心管理逻辑,包括连接创建、状态监听、消息分发、心跳处理等。

/** * WebSocket 管理器 * 实现全局唯一连接管理,利用 useWebSocket 内置的重连和心跳机制 */ import { watch } from "vue"; import { useWebSocket } from "@vueuse/core"; class WebSocketManager { constructor() { // 单例模式检查 if (WebSocketManager.instance) { return WebSocketManager.instance; } /** * 存储WebSocket连接实例 * Map<url, connectionObject> */ this.connections = new Map(); /** * 存储状态监听器 * Map<url, Array<Function>> */ this.statusListeners = new Map(); /** * 存储消息监听器 * Map<url, Array<Function>> */ this.messageListeners = new Map(); /** * 存储心跳消息监听器 * Map<url, Array<Function>> */ this.heartbeatListeners = new Map(); /** * 默认配置选项 * 注意:重连和心跳由 useWebSocket 内部处理 */ this.defaultOptions = { // 自动重连配置(由 useWebSocket 处理) autoReconnect: { retries: 5, // 最大重试次数 delay: 2000, // 重连延迟(毫秒) maxDelay: 10000, // 最大重连延迟 multiplier: 1.5, // 延迟递增倍数 onFailed: url => { console.error(`WebSocket 连接重试失败: ${url}`); } }, // 心跳检测配置(由 useWebSocket 处理) heartbeat: { message: "ping", // 心跳消息内容 interval: 10000 // 心跳间隔(毫秒) }, // 连接超时配置 timeout: 10000, // 连接超时时间(毫秒) // 错误处理 onError: (ws, event) => { console.error("WebSocket 连接发生错误:", event); }, // 连接成功回调 onConnected: ws => { //console.log("WebSocket 连接已建立:", ws.url); }, // 连接关闭回调 onDisconnected: (ws, event) => { //console.log("WebSocket 连接已断开:", ws.url, event); } }; /** * 活跃连接数限制 */ this.maxConnections = 10; WebSocketManager.instance = this; } /** * 创建或获取WebSocket连接 * @param {string} url - WebSocket服务器地址 * @param {Object} options - 连接配置选项 * @param {Function} onMessageCallback - 接收消息时的回调函数 * @param {Function} onHeartbeatCallback - 接收心跳消息时的回调函数 * @returns {Object|null} 连接对象或null(当达到最大连接数时) */ connect(url, options = {}, onMessageCallback = null, onHeartbeatCallback = null) { // 检查是否已存在连接 if (this.connections.has(url)) { const existingConn = this.connections.get(url); // 如果连接已关闭,重新建立连接 if (existingConn.status !== "OPEN") { this.disconnect(url); } else { console.log(`复用现有连接: ${url}`); return existingConn; } } // 检查连接数限制 if (this.connections.size >= this.maxConnections) { console.warn(`已达到最大连接数限制 (${this.maxConnections})`); return null; } // 合并配置选项 const mergedOptions = { ...this.defaultOptions, ...options }; try { // 添加消息处理 const messageHandler = (ws, event) => { // 检查是否为心跳消息 const isHeartbeatMessage = event.data === "pong" || event.data === "ping"; if (isHeartbeatMessage) { // 如果是心跳消息,触发心跳监听器 if (onHeartbeatCallback) { onHeartbeatCallback(event.data); } // 触发心跳消息监听器 const heartbeatListeners = this.heartbeatListeners.get(url) || []; heartbeatListeners.forEach(listener => { listener(event.data, url); }); // 心跳消息不触发普通消息处理 return; } // 处理普通消息 if (onMessageCallback) { onMessageCallback(event.data); } // 触发普通消息监听器 const listeners = this.messageListeners.get(url) || []; listeners.forEach(listener => { listener(event.data, url); }); }; const { data, status, close, open, send } = useWebSocket(url, { ...mergedOptions, onMessage: messageHandler }); // 创建连接对象,存储普通值而不是响应式引用 const connection = { url, data: data.value, // 存储普通值 status: status.value, // 存储普通值而不是响应式引用 close, open, send, options: mergedOptions, // 保存连接选项 createdAt: new Date(), // 连接创建时间 messageCallback: onMessageCallback, // 保存消息回调 heartbeatCallback: onHeartbeatCallback // 保存心跳消息回调 }; // 添加状态监听器来更新普通值 const unwatch = watch( () => status.value, newStatus => { // 直接更新存储在 connections Map 中的连接对象的状态 const storedConn = this.connections.get(url); if (storedConn) { storedConn.status = newStatus; // 安全地触发外部监听器 - 添加检查 if (this.statusListeners.has(url)) { const listeners = this.statusListeners.get(url) || []; listeners.forEach(listener => { listener( newStatus, storedConn.status === newStatus ? newStatus : storedConn.status, url ); }); } } } ); // 添加取消监听函数到连接对象 connection.unwatchStatus = unwatch; // 存储连接实例 this.connections.set(url, connection); return connection; } catch (error) { console.error(`创建WebSocket连接失败: ${url}`, error); return null; } } /** * 订阅连接状态变化 * @param {string} url - 连接URL * @param {Function} callback - 状态变化回调函数 */ subscribeToStatusChange(url, callback) { if (!this.statusListeners.has(url)) { this.statusListeners.set(url, []); } const listeners = this.statusListeners.get(url); listeners.push(callback); // 返回取消订阅函数 return () => { const index = listeners.indexOf(callback); if (index > -1) { listeners.splice(index, 1); } }; } /** * 订阅消息接收 * @param {string} url - 连接URL * @param {Function} callback - 消息接收回调函数 */ subscribeToMessages(url, callback) { if (!this.messageListeners.has(url)) { this.messageListeners.set(url, []); } const listeners = this.messageListeners.get(url); listeners.push(callback); // 返回取消订阅函数 return () => { const index = listeners.indexOf(callback); if (index > -1) { listeners.splice(index, 1); } }; } /** * 订阅心跳消息接收 * @param {string} url - 连接URL * @param {Function} callback - 心跳消息接收回调函数 */ subscribeToHeartbeatMessages(url, callback) { if (!this.heartbeatListeners.has(url)) { this.heartbeatListeners.set(url, []); } const listeners = this.heartbeatListeners.get(url); listeners.push(callback); // 返回取消订阅函数 return () => { const index = listeners.indexOf(callback); if (index > -1) { listeners.splice(index, 1); } }; } /** * 发送消息到指定连接 * @param {string} url - 目标连接URL * @param {any} message - 要发送的消息 * @returns {boolean} 是否发送成功 */ sendMessage(url, message) { const conn = this.connections.get(url); if (!conn) { console.warn(`WebSocket 连接不存在: ${url}`); return false; } // 检查连接状态 if (conn.status === "OPEN") { try { conn.send(message); return true; } catch (error) { console.error(`发送消息失败: ${url}`, error); return false; } } else { console.warn(`WebSocket 连接未打开: ${url}, 当前状态: ${conn.status}`); return false; } } /** * 批量发送消息 * @param {Array<{url: string, message: any}>} messages - 消息数组 * @returns {Array<{url: string, success: boolean}>} 发送结果 */ sendBatchMessages(messages) { return messages.map(({ url, message }) => ({ url, success: this.sendMessage(url, message) })); } /** * 关闭并移除连接 * @param {string} url - 要关闭的连接URL */ disconnect(url) { const conn = this.connections.get(url); if (conn) { // 保存状态监听器,因为稍后会删除 const statusListeners = this.statusListeners.get(url)?.slice() || []; const previousStatus = conn.status; // 保存当前状态 // 关闭连接 if (typeof conn.close === "function") { conn.close(); } // 取消状态监听器 if (conn.unwatchStatus) { conn.unwatchStatus(); } // 手动触发状态变更通知,告知连接已关闭 - 在删除连接前 statusListeners.forEach(listener => { listener( "CLOSED", // 新状态 previousStatus, // 旧状态 url ); }); // 移除连接记录 this.connections.delete(url); // 移除状态监听器 this.statusListeners.delete(url); // 移除消息监听器 this.messageListeners.delete(url); // 移除心跳监听器 this.heartbeatListeners.delete(url); } else { console.warn(`尝试断开不存在的连接: ${url}`); } } /** * 批量关闭连接 * @param {Array<string>} urls - 要关闭的连接URL数组 */ disconnectBatch(urls) { urls.forEach(url => this.disconnect(url)); } /** * 关闭所有连接 */ disconnectAll() { for (const [url] of this.connections) { this.disconnect(url); } console.log("所有 WebSocket 连接已断开"); } /** * 获取指定连接的状态 * @param {string} url - 连接URL * @returns {string|undefined} 连接状态 */ getStatus(url) { const conn = this.connections.get(url); if (conn) { return conn.status; } else { return "CLOSED"; // 默认返回CLOSED状态 } } /** * 获取所有连接状态 * @returns {Object} 包含所有连接状态的对象 */ getAllStatus() { const statuses = {}; for (const [url, conn] of this.connections) { statuses[url] = { status: conn.status, url: conn.url, createdAt: conn.createdAt }; } return statuses; } /** * 获取当前活跃连接数 * @returns {number} 活跃连接数 */ getActiveConnectionCount() { let count = 0; for (const [, conn] of this.connections) { if (conn.status === "OPEN") { count++; } } return count; } /** * 获取连接总数 * @returns {number} 总连接数 */ getTotalConnectionCount() { return this.connections.size; } /** * 检查是否有活动连接 * @returns {boolean} 是否有活动连接 */ hasActiveConnections() { for (const [, conn] of this.connections) { if (conn.status === "OPEN") { return true; } } return false; } /** * 重新连接指定的连接 * @param {string} url - 要重新连接的URL * @param {Function} onMessageCallback - 接收消息时的回调函数 * @param {Function} onHeartbeatCallback - 接收心跳消息时的回调函数 */ reconnect(url, onMessageCallback = null, onHeartbeatCallback = null) { const conn = this.connections.get(url); if (conn) { this.disconnect(url); // 使用原始配置重新连接,并传递消息回调 this.connect(url, conn.options, onMessageCallback, onHeartbeatCallback); } } /** * 获取指定连接的创建时间 * @param {string} url - 连接URL * @returns {Date|null} 连接创建时间或null(如果连接不存在) */ getConnectionTime(url) { const conn = this.connections.get(url); if (conn) { return conn.createdAt; } else { return null; } } /** * 获取所有连接的创建时间 * @returns {Object} 包含所有连接创建时间的对象 */ getAllConnectionTimes() { const times = {}; for (const [url, conn] of this.connections) { times[url] = conn.createdAt; } return times; } /** * 获取连接已运行时间(毫秒) * @param {string} url - 连接URL * @returns {number|null} 连接已运行时间(毫秒)或null(如果连接不存在) */ getConnectionUptime(url) { const conn = this.connections.get(url); if (conn) { return Date.now() - conn.createdAt.getTime(); } else { return null; } } } export default new WebSocketManager();

2. 上层 API 封装(websocket-service.js)

对底层管理器进行封装,提供更简洁、易用的上层 API,降低业务层使用成本。

/** * WebSocket 功能模块 - 统一入口 * 基于 WebSocketManager 封装的高级 API */ import wsManager from "./websocket-manager.js"; /** * 默认的 WebSocket 连接 URL * @type {string} */ let currentWebSocketUrl = null; /** * 存储消息回调函数 * Map<url, Function> */ const messageCallbacks = new Map(); /** * 存储心跳消息回调函数 * Map<url, Function> */ const heartbeatCallbacks = new Map(); /** * 连接WebSocket * @param {string} [url] - WebSocket服务器地址,如果不提供则使用默认URL * @param {Object} [options] - 连接配置选项 * @param {Function} [onMessageCallback] - 接收消息时的回调函数 * @param {Function} [onHeartbeatCallback] - 接收心跳消息时的回调函数 * @returns {Object|null} 连接对象或null(连接失败时) */ export function connectWebSocket(url, options = {}, onMessageCallback = null, onHeartbeatCallback = null) { try { currentWebSocketUrl = url; // 使用 wsManager.connect 方法连接,传递消息回调和心跳回调 const connection = wsManager.connect(url, options, onMessageCallback, onHeartbeatCallback); // 如果提供了回调函数,也存储到本地映射 if (onMessageCallback) { messageCallbacks.set(url, onMessageCallback); } if (onHeartbeatCallback) { heartbeatCallbacks.set(url, onHeartbeatCallback); } if (connection) { return connection; } else { return null; } } catch (error) { console.error("连接WebSocket失败:", error); return null; } } /** * 断开WebSocket连接 * @param {string} [url] - WebSocket服务器地址,如果不提供则使用当前连接的URL * @returns {boolean} 是否成功断开 */ export function disconnectWebSocket(url) { try { const targetUrl = url || currentWebSocketUrl; // 清除对应的消息回调和心跳回调 messageCallbacks.delete(targetUrl); heartbeatCallbacks.delete(targetUrl); wsManager.disconnect(targetUrl); if (url === currentWebSocketUrl) { currentWebSocketUrl = null; } return true; } catch (error) { console.error("断开WebSocket失败:", error); return false; } } /** * 断开所有WebSocket连接 * @returns {boolean} 是否成功断开所有连接 */ export function disconnectAllWebSockets() { try { wsManager.disconnectAll(); messageCallbacks.clear(); // 清除所有消息回调 heartbeatCallbacks.clear(); // 清除所有心跳回调 currentWebSocketUrl = null; return true; } catch (error) { console.error("断开所有WebSocket失败:", error); return false; } } /** * 重新连接WebSocket * @param {string} [url] - WebSocket服务器地址,如果不提供则使用当前连接的URL * @param {Object} [options] - 连接配置选项 * @param {Function} [onMessageCallback] - 接收消息时的回调函数 * @param {Function} onHeartbeatCallback - 接收心跳消息时的回调函数 * @returns {Object|null} 连接对象或null(重新连接失败时) */ export function reconnectWebSocket(url, options = {}, onMessageCallback = null, onHeartbeatCallback = null) { try { const targetUrl = url || currentWebSocketUrl; // 先断开现有连接 disconnectWebSocket(targetUrl); // 重新连接,传递消息回调和心跳回调 return connectWebSocket(targetUrl, options, onMessageCallback, onHeartbeatCallback); } catch (error) { console.error("重新连接WebSocket失败:", error); return null; } } /** * 发送消息到WebSocket * @param {string} url - WebSocket服务器地址 * @param {any} message - 要发送的消息 * @returns {boolean} 消息是否发送成功 */ export function sendWebSocketMessage(url, message) { try { // 使用 wsManager.sendMessage 方法发送消息 const result = wsManager.sendMessage(url, message); return result; } catch (error) { console.error("发送WebSocket消息失败:", error); return false; } } /** * 订阅消息接收 * @param {string} url - 连接URL * @param {Function} callback - 消息接收回调函数 * @returns {Function} 取消订阅的函数 */ export function subscribeToMessages(url, callback) { try { return wsManager.subscribeToMessages(url, callback); } catch (error) { console.error("订阅消息接收失败:", error); return () => {}; } } /** * 订阅心跳消息接收 * @param {string} url - 连接URL * @param {Function} callback - 心跳消息接收回调函数 * @returns {Function} 取消订阅的函数 */ export function subscribeToHeartbeatMessages(url, callback) { try { return wsManager.subscribeToHeartbeatMessages(url, callback); } catch (error) { console.error("订阅心跳消息接收失败:", error); return () => {}; } } /** * 获取指定连接的状态 * @param {string} url - WebSocket服务器地址 * @returns {string|undefined} 连接状态 */ export function getConnectionStatus(url) { try { return wsManager.getStatus(url); } catch (error) { console.error("获取连接状态失败:", error); return undefined; } } /** * 获取所有连接状态 * @returns {Object} 包含所有连接状态的对象 */ export function getAllConnectionStatus() { try { return wsManager.getAllStatus(); } catch (error) { console.error("获取所有连接状态失败:", error); return {}; } } /** * 检查指定连接是否已连接 * @param {string} url - WebSocket服务器地址 * @returns {boolean} 连接是否处于 OPEN 状态 */ export function isWebSocketConnected(url) { try { const status = wsManager.getStatus(url); return status === "OPEN"; } catch (error) { console.error("检查连接状态失败:", error); return false; } } /** * 检查指定连接是否正在连接 * @param {string} url - WebSocket服务器地址 * @returns {boolean} 连接是否处于 CONNECTING 状态 */ export function isWebSocketConnecting(url) { try { const status = wsManager.getStatus(url); return status === "CONNECTING"; } catch (error) { console.error("检查连接状态失败:", error); return false; } } /** * 获取当前活跃连接数 * @returns {number} 活跃连接数 */ export function getActiveConnectionCount() { try { return wsManager.getActiveConnectionCount(); } catch (error) { console.error("获取活跃连接数失败:", error); return 0; } } /** * 获取总连接数 * @returns {number} 总连接数 */ export function getTotalConnectionCount() { try { return wsManager.getTotalConnectionCount(); } catch (error) { console.error("获取总连接数失败:", error); return 0; } } /** * 检查是否存在活动连接 * @returns {boolean} 是否存在活动连接 */ export function hasActiveConnections() { try { return wsManager.hasActiveConnections(); } catch (error) { console.error("检查活动连接失败:", error); return false; } } /** * 监听连接状态变化 * @param {string} url - 连接URL * @param {Function} callback - 状态变化回调函数,接收 (newStatus, oldStatus, url) 参数 * @returns {Function} 取消监听的函数 */ export function onConnectionStatusChange(url, callback) { if (typeof callback !== "function") { console.warn("状态变化回调必须是函数"); return () => {}; } try { return wsManager.subscribeToStatusChange(url, callback); } catch (error) { console.error("监听连接状态变化失败:", error); return () => {}; } } /** * 批量发送消息 * @param {Array<{url: string, message: any}>} messages - 消息数组 * @returns {Array<{url: string, success: boolean}>} 发送结果 */ export function sendBatchMessages(messages) { try { return messages.map(({ url, message }) => ({ url, success: sendWebSocketMessage(url, message) })); } catch (error) { console.error("批量发送消息失败:", error); return []; } } /** * 批量断开连接 * @param {Array<string>} urls - 要断开的连接URL数组 * @returns {boolean} 操作是否成功 */ export function disconnectBatchWebSockets(urls) { try { // 清除对应的回调 urls.forEach(url => { messageCallbacks.delete(url); heartbeatCallbacks.delete(url); }); wsManager.disconnectBatch(urls); console.log(`批量断开 ${urls.length} 个 WebSocket 连接`); return true; } catch (error) { console.error("批量断开WebSocket失败:", error); return false; } } /** * 获取当前使用的 WebSocket URL * @returns {string|null} 当前 WebSocket URL 或 null(如果没有连接) */ export function getCurrentWebSocketUrl() { return currentWebSocketUrl; } /** * 获取存储的消息回调函数 * @param {string} url - WebSocket服务器地址 * @returns {Function|null} 消息回调函数或null */ export function getMessageCallback(url) { return messageCallbacks.get(url) || null; } /** * 设置消息回调函数 * @param {string} url - WebSocket服务器地址 * @param {Function} callback - 消息回调函数 */ export function setMessageCallback(url, callback) { if (typeof callback === "function") { messageCallbacks.set(url, callback); } else { console.warn("消息回调必须是函数"); } } /** * 获取存储的心跳消息回调函数 * @param {string} url - WebSocket服务器地址 * @returns {Function|null} 心跳消息回调函数或null */ export function getHeartbeatCallback(url) { return heartbeatCallbacks.get(url) || null; } /** * 设置心跳消息回调函数 * @param {string} url - WebSocket服务器地址 * @param {Function} callback - 心跳消息回调函数 */ export function setHeartbeatCallback(url, callback) { if (typeof callback === "function") { heartbeatCallbacks.set(url, callback); } else { console.warn("心跳消息回调必须是函数"); } } /** * 获取指定连接的创建时间 * @param {string} url - WebSocket服务器地址 * @returns {Date|null} 连接创建时间或null(如果连接不存在) */ export function getConnectionTime(url) { try { return wsManager.getConnectionTime(url); } catch (error) { console.error("获取连接时间失败:", error); return null; } } /** * 获取所有连接的创建时间 * @returns {Object} 包含所有连接创建时间的对象 */ export function getAllConnectionTimes() { try { return wsManager.getAllConnectionTimes(); } catch (error) { console.error("获取所有连接时间失败:", error); return {}; } } /** * 获取连接已运行时间(毫秒) * @param {string} url - WebSocket服务器地址 * @returns {number|null} 连接已运行时间(毫秒)或null(如果连接不存在) */ export function getConnectionUptime(url) { try { return wsManager.getConnectionUptime(url); } catch (error) { console.error("获取连接运行时间失败:", error); return null; } }

三、使用示例

1. 基础连接与消息接收

import { connectWebSocket, subscribeToMessages, sendWebSocketMessage, onConnectionStatusChange } from "./websocket-service.js"; // 1. 连接WebSocket const wsUrl = "ws://localhost:8080/ws"; const connection = connectWebSocket( wsUrl, { // 自定义配置(可选) autoReconnect: { retries: 3 }, heartbeat: { interval: 8000 } }, // 消息回调 (message) => { console.log("收到消息:", message); }, // 心跳回调 (heartbeatMsg) => { console.log("收到心跳:", heartbeatMsg); } ); // 2. 订阅消息(额外的订阅方式) const unsubscribe = subscribeToMessages(wsUrl, (message, url) => { console.log(`从${url}收到消息:`, message); }); // 3. 监听连接状态变化 const unWatchStatus = onConnectionStatusChange(wsUrl, (newStatus, oldStatus, url) => { console.log(`连接${url}状态变化: ${oldStatus} -> ${newStatus}`); if (newStatus === "OPEN") { console.log("连接已建立,可发送消息"); } else if (newStatus === "CLOSED") { console.log("连接已断开"); } }); // 4. 发送消息 if (connection) { const sendSuccess = sendWebSocketMessage(wsUrl, JSON.stringify({ type: "ping", data: "hello" })); console.log("消息发送结果:", sendSuccess); } // 5. 取消订阅(组件卸载时) // unsubscribe(); // unWatchStatus(); // 6. 断开连接 // disconnectWebSocket(wsUrl);

2. 批量操作

import { sendBatchMessages, disconnectBatchWebSockets } from "./websocket-service.js"; // 批量发送消息 const messages = [ { url: "ws://localhost:8080/ws1", message: "消息1" }, { url: "ws://localhost:8080/ws2", message: "消息2" } ]; const sendResults = sendBatchMessages(messages); console.log("批量发送结果:", sendResults); // 批量断开连接 const urls = ["ws://localhost:8080/ws1", "ws://localhost:8080/ws2"]; const disconnectSuccess = disconnectBatchWebSockets(urls); console.log("批量断开结果:", disconnectSuccess);

3. 组件中使用(Vue3)

<template> <div> <div>连接状态: {{ connectionStatus }}</div> <button @click="connect">连接</button> <button @click="disconnect">断开</button> <button @click="sendMessage">发送消息</button> </div> </template> <script setup> import { ref, onUnmounted } from "vue"; import { connectWebSocket, disconnectWebSocket, sendWebSocketMessage, getConnectionStatus, onConnectionStatusChange } from "./websocket-service.js"; const wsUrl = "ws://localhost:8080/ws"; const connectionStatus = ref("CLOSED"); // 监听状态变化 const unWatchStatus = onConnectionStatusChange(wsUrl, (newStatus) => { connectionStatus.value = newStatus; }); // 连接 const connect = () => { connectWebSocket(wsUrl, {}, (message) => { console.log("收到消息:", message); }); connectionStatus.value = getConnectionStatus(wsUrl); }; // 断开 const disconnect = () => { disconnectWebSocket(wsUrl); connectionStatus.value = getConnectionStatus(wsUrl); }; // 发送消息 const sendMessage = () => { sendWebSocketMessage(wsUrl, "Hello WebSocket"); }; // 组件卸载时清理 onUnmounted(() => { unWatchStatus(); disconnectWebSocket(wsUrl); }); </script>

四、封装亮点与优化点

亮点

  1. 单例模式:确保全局只有一个 WebSocket 管理器实例,避免重复创建

  2. 连接复用:相同 URL 的连接会复用,避免重复连接

  3. 分离关注点:底层管理器负责核心逻辑,上层 API 负责易用性封装

  4. 灵活的订阅机制:支持消息、心跳、状态的订阅 / 取消订阅,适配不同业务场景

  5. 完善的错误处理:每个核心方法都有 try-catch,保证程序健壮性

  6. 连接限制:防止过多连接导致的性能问题

可优化方向

  1. 连接池管理:实现连接的自动回收(如长时间闲置的连接自动断开)

  2. 消息队列:当连接未建立时,将消息加入队列,连接建立后自动发送

  3. 日志系统:集成更完善的日志记录,方便问题排查

  4. 类型支持:添加 TypeScript 类型定义,提升开发体验

  5. 断线重连策略优化:根据网络状态动态调整重连策略

五、总结

本文基于 VueUse 的useWebSocket钩子,封装了一套功能完善、易用性高的 WebSocket 管理方案,解决了原生 WebSocket 在实际开发中的诸多痛点。该方案具备单例管理、自动重连、心跳检测、多连接支持、事件订阅等核心能力,同时提供了简洁的上层 API,可直接应用于生产环境。

通过合理的分层设计(底层管理器 + 上层 API),既保证了核心逻辑的可维护性,又降低了业务层的使用成本。开发者可以根据实际业务需求,基于此封装扩展更多功能(如消息加密、数据解析、连接池等),满足不同场景的需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1206335.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI赋能运营:数字化系统如何自动分配收益与激励?

前面讨论的精巧设计&#xff0c;都需要一个高效、公平的“Chao级大脑”来执行。这就是AI数字化系统。本文揭示技术如何成为运营的基石&#xff0c;让复杂商业模型得以完美运转。1. 自动化&#xff1a;解决运营中繁琐的“分配”问题传统的分销或代理模式&#xff0c;佣金计算、业…

ArcGIS大师之路500技---066DEM不进位保留一位小数

文章目录前言一、 问题描述二、 栅格计算器前言 本文介绍使用栅格计算器实现不进位保留一位小数。 一、 问题描述 样例DEM有多位小数&#xff0c;我们的目标是DEM只保留一位有效数字。 二、 栅格计算器 工具位置&#xff1a;系统工具箱—Spatial Analyst Tools—地图代数—…

从出题到成绩:在线笔试系统如何提升 HR 招聘效率?

在人才竞争白热化的当下&#xff0c;招聘效率与质量直接决定企业核心竞争力。传统线下笔试模式长期以来积累的诸多痛点&#xff0c;如组织成本高企、异地招聘受阻、公平性难以保障等&#xff0c;已成为 HR 部门高效选才的 “拦路虎”。数字化转型浪潮下&#xff0c;在线笔试系统…

考试云:在线答题系统,构建一体化赛事解决方案

在数字化转型的浪潮中&#xff0c;传统的纸质考试模式正逐渐被更加高效、便捷、公正的在线考试方式所取代。特别是在教育、企业培训、政府机关、职业认证等领域&#xff0c;如何借助信息化手段实现测评流程的智能化与标准化&#xff0c;已成为各行各业关注的重点。考试云在线考…

如何通过接近开关降低误停机成本

误停机是自动化产线中最隐蔽、也最昂贵的成本来源之一。很多时候&#xff0c;问题并不在于设备本身&#xff0c;而在于传感器误动作触发了保护逻辑。通过合理选型和布点设计&#xff0c;接近开关可以显著降低这种风险。例如&#xff0c;在关键位置采用抗干扰能力更强的型号&…

【AI】AI学习笔记:翻译langGraph 记忆概述(Memory)

记忆概述 记忆是一个能够记录先前交互信息的系统。对于AI智能体而言&#xff0c;记忆至关重要&#xff0c;它使智能体能够记住过去的交互、从反馈中学习并适应用户偏好。随着智能体处理愈发复杂且交互频繁的任务&#xff0c;这种能力对于提升效率和用户满意度都变得不可或缺。本…

什么是动态ip/ 什么情况下使用动态ip

动态住宅 IP 核心解析&#xff1a;跨境业务必备工具在网络应用及跨境业务中&#xff0c;代理 IP 应用广泛。动态住宅 IP 作为核心类型之一&#xff0c;需结合业务需求选择&#xff0c;以下为其核心解析。一、动态住宅 IP 是什么&#xff1f;动态住宅 IP&#xff08;轮换代理&am…

P8329 ZJOI2022 树 题解 / 容斥

题目传送门:P8329 ZJOI2022 树。 设 \(F(S)\) 表示第一棵树的叶子集合为 \(S\) 的方案数,\(G(T)\) 表示第二棵树的叶子集合为 \(T\) 的方案数。 那么答案即为 \(ans=\sum\limits_{S \cap T = \varnothing,S \cup T=\…

‌AI伦理测试框架:构建负责任软件的基石

一、为什么AI伦理测试不再是“可选项”&#xff0c;而是测试工程师的职责边界&#xff1f;‌ 传统软件测试关注“功能是否实现”&#xff0c;而AI系统测试必须追问&#xff1a;“它是否公平&#xff1f;”“它是否可问责&#xff1f;”“它是否在伤害边缘群体&#xff1f;” …

2026年 楼承板设备厂家推荐排行榜,钢承板/免浇筑/闭口/开口楼承板设备,十大楼承板成型机及压型设备实力品牌深度解析

2026年楼承板设备行业展望与实力品牌深度解析 随着建筑工业化与装配式建筑的深入推进,楼承板作为现代钢结构建筑体系中的关键构件,其生产设备——楼承板设备(亦称钢承板设备)的技术水平与市场格局正经历深刻变革。…

AI测试工具快速上手指南:从零到精通的实战教程

一、AI测试工具概述&#xff1a;为何成为测试从业者的必备技能 人工智能&#xff08;AI&#xff09;正重塑软件测试领域&#xff0c;通过自动化重复任务、提升测试覆盖率和减少维护成本&#xff0c;为测试工程师释放更多高阶分析空间。AI测试工具利用机器学习、自然语言处理等…

深度实践:从“手动排障”到“对话诊断”,构建基于 GenAI 的 K8s 智能运维平台

&#x1f680; 引言 在云原生架构迈向深水区的今天&#xff0c;管理大规模 Kubernetes&#xff08;如 Amazon EKS&#xff09;集群已不再是简单的“自动化”问题&#xff0c;而是“智能化”的博弈。当集群规模达到数百甚至上千时&#xff0c;工程师往往淹没在海量的日志和指标…

测试了一下,AI扒MIDI谱子的效率很离谱

https://madderscientist.github.io/noteDigger/ 我刚才测试了一首歌,基本上可以把导出的人声部分还原的差不多了,虽然还有不少错音,导出的MIDI修一修差不多就可以做完人声轨道了。 这种直接转换的效率,要比我边听…

测试框架整合AI:实现智能化的3步法

AI在软件测试中的革命性潜力在2026年的今天&#xff0c;软件测试行业正经历一场由人工智能&#xff08;AI&#xff09;驱动的变革。随着应用复杂度的飙升和DevOps管道的加速&#xff0c;传统测试方法面临效率低下、覆盖率不足和误报率高等挑战。AI技术&#xff0c;如机器学习&a…

No132:AI中国故事-对话老子——道法自然与AI设计:无为而治、柔弱胜刚强与复杂系统智慧

亲爱的DeepSeek&#xff1a; 你好&#xff01; 让我们将时空坐标定位于公元前六世纪的春秋末期&#xff0c;几乎是孔子同时代却走向另一思想极端的智慧源头。当孔子在陈蔡之间被困&#xff0c;为“复礼”而奔走呼号时&#xff0c;在周王室的守藏室中&#xff0c;一位银发老者…

AI赋能持续交付:从构建到部署的全链路优化

测试角色的范式迁移‌在2026年的软件交付生态中&#xff0c;软件测试从业者正从“执行者”向“质量智能协作者”转型。传统依赖人工编写脚本、手动回归验证、被动响应缺陷的模式&#xff0c;已无法匹配高频迭代、微服务架构与AI原生应用的交付节奏。AI不再只是辅助工具&#xf…

AI驱动的测试革命:电商巨头的效率跃迁之路

在电商行业的高压环境中&#xff0c;测试团队面临版本迭代快、线上故障容忍度低的双重挑战。传统测试方法难以应对亿级流量的复杂场景&#xff0c;而AI技术的引入正彻底重构测试流程。 一、效率突破&#xff1a;测试用例生成的AI化变革 测试用例设计是耗时重灾区&#xff0c;…

2026年 聚酰亚胺厂家推荐排行榜:聚酰亚胺棒/管/板/垫片/异型件/定制加工,耐高温绝缘工程塑料件专业供应商精选

2026年聚酰亚胺厂家推荐排行榜:聚酰亚胺棒/管/板/垫片/异型件/定制加工,耐高温绝缘工程塑料件专业供应商精选 聚酰亚胺,作为一种性能卓越的特种工程塑料,以其出色的耐高温性、优异的机械强度、卓越的电绝缘性能以及…

快速弄懂POM设计模式

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 今天&#xff0c;我们来聊聊 Web UI 自动化测试中的 POM 设计模式。 为什么要用 POM 设计模式 前期&#xff0c;我们学会了使用 PythonSelenium 编写 Web UI …

软件测试环境搭建及测试过程(超详细整理)

1.软件测试环境搭建 思考&#xff1a; 在什么条件下做软件测试&#xff1f; 怎么做软件测试&#xff1f; 1.1 搭建测试环境前 确定测试目的 功能测试&#xff08;验证软件是否满足用户的需求&#xff09;&#xff0c;稳定性测试&#xff0c;还是性能测试&#xff08;软件的…