告别socket.io!uni-app原生WebSocket封装实战(含心跳保活与全局调用)

张开发
2026/4/19 10:06:21 15 分钟阅读

分享文章

告别socket.io!uni-app原生WebSocket封装实战(含心跳保活与全局调用)
告别socket.iouni-app原生WebSocket封装实战含心跳保活与全局调用在移动应用开发中实时通信能力已经成为提升用户体验的关键要素。对于uni-app开发者而言WebSocket是实现这一功能的常见选择。然而许多开发者最初可能会倾向于使用socket.io等第三方库却在后续开发中遇到兼容性、配置复杂度或性能瓶颈等问题。本文将带你深入探索原生WebSocket在uni-app中的完整封装方案从基础连接到高级功能实现为你提供一个稳定、高效的实时通信解决方案。1. 为什么选择原生WebSocket而非第三方库在开始技术实现之前我们需要明确一个核心问题为什么要在uni-app中放弃socket.io等成熟方案转而使用原生WebSocket这个决策背后有几个关键考量因素性能与效率对比原生WebSocket直接使用浏览器/小程序环境提供的API没有额外的抽象层socket.io在握手阶段需要多次HTTP请求交换增加了连接建立的延迟原生方案传输数据量更小没有协议开销特别适合移动端弱网环境兼容性差异原生WebSocket在各平台的支持度已经非常完善包括微信小程序、H5和App端某些第三方库在小程序环境中可能存在意外的行为差异或功能限制原生方案更容易与各种后端技术栈Java、Go、Node.js等直接对接控制力与灵活性原生实现让你完全掌控连接生命周期和错误处理逻辑可以针对特定业务需求定制重连策略和心跳机制避免了第三方库版本升级带来的潜在兼容性风险提示虽然原生WebSocket需要更多开发工作但长远来看这种投入会带来更稳定的通信基础和更好的性能表现。2. WebSocket核心封装类设计下面我们将构建一个完整的WebSocket工具类它不仅包含基础连接功能还整合了心跳检测、自动重连等企业级应用所需的特性。class WebSocketManager { constructor(url, options {}) { // 配置参数 this.url url this.heartbeatInterval options.heartbeatInterval || 30000 this.reconnectDelay options.reconnectDelay || 5000 this.maxReconnectAttempts options.maxReconnectAttempts || 5 // 状态变量 this.socketTask null this.isConnected false this.reconnectAttempts 0 this.heartbeatTimer null this.reconnectTimer null // 消息处理器 this.messageHandlers new Set() // 初始化连接 this.connect() } // 建立WebSocket连接 connect() { if (this.isConnected) return this.socketTask uni.connectSocket({ url: this.url, complete: () {} }) this.bindEvents() } // 绑定事件监听 bindEvents() { this.socketTask.onOpen(() { this.handleOpen() }) this.socketTask.onClose(() { this.handleClose() }) this.socketTask.onError((err) { this.handleError(err) }) this.socketTask.onMessage((res) { this.handleMessage(res) }) } // 连接成功处理 handleOpen() { this.isConnected true this.reconnectAttempts 0 this.startHeartbeat() console.log(WebSocket连接已建立) } // 连接关闭处理 handleClose() { this.isConnected false this.clearHeartbeat() this.scheduleReconnect() console.log(WebSocket连接已关闭) } // 错误处理 handleError(error) { console.error(WebSocket错误:, error) this.handleClose() } // 消息处理 handleMessage(res) { try { const message JSON.parse(res.data) this.messageHandlers.forEach(handler handler(message)) } catch (e) { console.error(消息解析错误:, e) } } // 发送消息 send(data) { if (!this.isConnected) { console.warn(尝试发送消息但连接未就绪) return false } return new Promise((resolve, reject) { this.socketTask.send({ data: JSON.stringify(data), success: () resolve(), fail: (err) reject(err) }) }) } // 开始心跳检测 startHeartbeat() { this.clearHeartbeat() const heartbeatData { type: heartbeat, timestamp: Date.now() } this.heartbeatTimer setInterval(() { if (this.isConnected) { this.send(heartbeatData).catch(err { console.error(心跳发送失败:, err) this.handleClose() }) } }, this.heartbeatInterval) } // 清除心跳检测 clearHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer) this.heartbeatTimer null } } // 安排重连 scheduleReconnect() { if (this.reconnectAttempts this.maxReconnectAttempts) { console.warn(已达到最大重连次数停止尝试) return } this.clearReconnect() this.reconnectTimer setTimeout(() { this.reconnectAttempts console.log(尝试重连(${this.reconnectAttempts}/${this.maxReconnectAttempts})...) this.connect() }, this.reconnectDelay) } // 清除重连定时器 clearReconnect() { if (this.reconnectTimer) { clearTimeout(this.reconnectTimer) this.reconnectTimer null } } // 添加消息处理器 addMessageHandler(handler) { this.messageHandlers.add(handler) return () this.removeMessageHandler(handler) } // 移除消息处理器 removeMessageHandler(handler) { this.messageHandlers.delete(handler) } // 关闭连接 close() { this.clearHeartbeat() this.clearReconnect() if (this.socketTask) { this.socketTask.close() this.socketTask null } this.isConnected false this.messageHandlers.clear() } }这个封装类提供了以下关键特性自动重连机制在连接意外断开时自动尝试重新连接心跳保活定期发送心跳包保持连接活跃消息统一处理支持多个消息处理器同时监听完善的错误处理对各种异常情况都有相应处理Promise风格API发送消息返回Promise便于异步处理3. uni-app中的集成方案在uni-app中我们有两种主要的使用方式单页面使用和全局使用。下面分别介绍这两种集成方案的具体实现。3.1 单页面使用方案在需要WebSocket功能的页面中直接实例化和使用import WebSocketManager from /utils/websocket export default { data() { return { socket: null, messages: [] } }, mounted() { // 初始化WebSocket连接 this.socket new WebSocketManager(wss://your-server.com/ws, { heartbeatInterval: 20000, reconnectDelay: 3000 }) // 添加消息处理器 const removeHandler this.socket.addMessageHandler((message) { this.messages.push(message) this.processMessage(message) }) // 示例发送登录消息 this.sendLogin() }, methods: { sendLogin() { const loginData { type: login, userId: user123, token: auth-token-here } this.socket.send(loginData) .then(() console.log(登录消息发送成功)) .catch(err console.error(发送登录消息失败:, err)) }, processMessage(message) { switch (message.type) { case chat: this.handleChatMessage(message) break case notification: this.handleNotification(message) break // 其他消息类型处理... } }, // 其他方法... }, beforeDestroy() { // 页面销毁时清理WebSocket if (this.socket) { this.socket.close() } } }3.2 全局使用方案对于需要在多个页面共享的WebSocket连接我们可以将其挂载到Vue原型上main.jsimport Vue from vue import App from ./App import WebSocketManager from ./utils/websocket // 创建WebSocket实例 const socket new WebSocketManager(wss://your-server.com/ws) // 挂载到Vue原型 Vue.prototype.$socket socket // 全局错误处理 socket.addMessageHandler((message) { if (message.type error) { uni.showToast({ title: message.content, icon: none }) } }) Vue.config.productionTip false App.mpType app const app new Vue({ ...App }) app.$mount()页面中使用export default { mounted() { // 添加当前页面的消息处理器 this.socketHandler this.$socket.addMessageHandler((message) { if (message.type chat) { this.handleChatMessage(message) } }) // 发送消息示例 this.$socket.send({ type: joinRoom, roomId: room123 }) }, beforeDestroy() { // 移除当前页面的消息处理器 if (this.socketHandler) { this.socketHandler() } }, methods: { handleChatMessage(message) { // 处理聊天消息... } } }4. 高级功能与最佳实践4.1 心跳机制优化基础的心跳检测虽然简单有效但在实际生产环境中可能需要更精细的控制// 在WebSocketManager类中添加以下方法 // 优化后的心跳检测 startHeartbeat() { this.clearHeartbeat() const heartbeatData { type: heartbeat, timestamp: Date.now() } let missedHeartbeats 0 const maxMissedHeartbeats 3 // 发送心跳 const sendHeartbeat () { if (!this.isConnected) return this.send(heartbeatData) .then(() { missedHeartbeats 0 }) .catch(err { console.error(心跳发送失败:, err) missedHeartbeats if (missedHeartbeats maxMissedHeartbeats) { console.warn(连续心跳失败主动关闭连接) this.handleClose() } }) } // 立即发送第一次心跳 sendHeartbeat() // 设置定时器 this.heartbeatTimer setInterval(sendHeartbeat, this.heartbeatInterval) }4.2 重连策略进阶简单的固定间隔重连可能不够理想我们可以实现指数退避算法// 在WebSocketManager构造函数中添加 this.initialReconnectDelay options.initialReconnectDelay || 1000 this.maxReconnectDelay options.maxReconnectDelay || 30000 // 修改scheduleReconnect方法 scheduleReconnect() { if (this.reconnectAttempts this.maxReconnectAttempts) { console.warn(已达到最大重连次数停止尝试) return } this.clearReconnect() // 指数退避计算延迟时间 const delay Math.min( this.initialReconnectDelay * Math.pow(2, this.reconnectAttempts), this.maxReconnectDelay ) this.reconnectTimer setTimeout(() { this.reconnectAttempts console.log(尝试重连(${this.reconnectAttempts}/${this.maxReconnectAttempts})延迟${delay}ms...) this.connect() }, delay) }4.3 消息队列与离线处理在网络不稳定时实现消息队列可以确保重要消息不丢失// 在WebSocketManager类中添加以下属性和方法 constructor(url, options {}) { // 新增属性 this.messageQueue [] this.maxQueueSize options.maxQueueSize || 50 this.flushQueueTimer null this.flushQueueInterval options.flushQueueInterval || 1000 } // 修改send方法 send(data) { return new Promise((resolve, reject) { if (!this.isConnected) { // 连接未就绪时加入队列 if (this.messageQueue.length this.maxQueueSize) { this.messageQueue.push({ data, resolve, reject }) this.scheduleQueueFlush() console.log(消息已加入队列等待连接恢复) } else { reject(new Error(消息队列已满)) } return } this.socketTask.send({ data: JSON.stringify(data), success: () resolve(), fail: (err) reject(err) }) }) } // 安排队列刷新 scheduleQueueFlush() { if (this.flushQueueTimer || !this.messageQueue.length) return this.flushQueueTimer setTimeout(() { this.flushQueue() this.flushQueueTimer null if (this.messageQueue.length) { this.scheduleQueueFlush() } }, this.flushQueueInterval) } // 刷新消息队列 flushQueue() { if (!this.isConnected || !this.messageQueue.length) return const queueCopy [...this.messageQueue] this.messageQueue [] queueCopy.forEach(({ data, resolve, reject }) { this.socketTask.send({ data: JSON.stringify(data), success: () resolve(), fail: (err) reject(err) }) }) } // 在handleOpen方法中添加队列刷新 handleOpen() { this.isConnected true this.reconnectAttempts 0 this.startHeartbeat() console.log(WebSocket连接已建立) // 连接建立后立即尝试刷新队列 this.flushQueue() }4.4 跨页面通信方案在复杂的应用中可能需要协调多个页面的WebSocket通信// 创建一个全局状态管理模块 websocket-store.js import Vue from vue const state Vue.observable({ connected: false, lastMessage: null, pendingActions: [] }) export const websocketStore { get connected() { return state.connected }, get lastMessage() { return state.lastMessage }, addPendingAction(action) { state.pendingActions.push(action) }, clearPendingActions() { state.pendingActions [] }, setConnected(status) { state.connected status }, setLastMessage(message) { state.lastMessage message } } // 修改WebSocketManager的全局集成方式 // main.js import { websocketStore } from ./stores/websocket-store // 创建WebSocket实例 const socket new WebSocketManager(wss://your-server.com/ws) // 状态同步 socket.addMessageHandler((message) { websocketStore.setLastMessage(message) }) socket.socketTask.onOpen(() { websocketStore.setConnected(true) // 执行pending actions websocketStore.pendingActions.forEach(action action()) websocketStore.clearPendingActions() }) socket.socketTask.onClose(() { websocketStore.setConnected(false) }) Vue.prototype.$socket socket页面中使用全局状态import { websocketStore } from /stores/websocket-store export default { computed: { isWebSocketConnected() { return websocketStore.connected }, lastWebSocketMessage() { return websocketStore.lastMessage } }, methods: { performWhenConnected(action) { if (this.isWebSocketConnected) { action() } else { websocketStore.addPendingAction(action) } } } }5. 调试与性能优化5.1 WebSocket调试技巧在实际开发中有效的调试方法可以大幅提高效率日志记录增强// 在WebSocketManager构造函数中添加 this.debug options.debug || false // 修改各个方法添加调试日志 handleOpen() { this.isConnected true this.reconnectAttempts 0 this.startHeartbeat() this.log(WebSocket连接已建立) if (this.debug) { uni.showToast({ title: WebSocket连接已建立, icon: none }) } } // 添加日志方法 log(message, data) { if (!this.debug) return const timestamp new Date().toISOString() console.log([WebSocket][${timestamp}] ${message}, data || ) }调试工具推荐使用Chrome开发者工具的Network面板查看WebSocket帧对于小程序环境利用微信开发者工具的调试功能使用Wireshark或Charles进行底层协议分析仅限开发环境5.2 性能优化建议消息压缩对于传输大量数据的场景可以考虑在发送前压缩数据// 在WebSocketManager中添加 this.useCompression options.useCompression || false // 修改send方法 async send(data) { let dataToSend data if (this.useCompression) { try { // 简单示例 - 实际项目中可使用更高效的压缩算法 dataToSend JSON.stringify(data) if (dataToSend.length 1024) { // 只压缩较大数据 dataToSend await this.compress(dataToSend) } } catch (e) { console.warn(压缩失败发送原始数据, e) } } // 发送逻辑... } // 简单压缩方法 - 实际项目中替换为更专业的实现 compress(data) { return new Promise((resolve) { // 这里使用简单Base64编码作为示例 // 实际项目中可使用pako等库进行gzip压缩 resolve(btoa(unescape(encodeURIComponent(data)))) }) }批量消息处理对于高频小消息可以考虑批量发送// 在WebSocketManager中添加 constructor() { this.batchInterval options.batchInterval || 100 this.batchMaxSize options.batchMaxSize || 10 this.batchQueue [] this.batchTimer null } // 添加批量发送方法 sendBatch(data) { this.batchQueue.push(data) if (this.batchQueue.length this.batchMaxSize) { this.flushBatch() return } if (!this.batchTimer) { this.batchTimer setTimeout(() { this.flushBatch() }, this.batchInterval) } } // 刷新批量队列 flushBatch() { if (this.batchTimer) { clearTimeout(this.batchTimer) this.batchTimer null } if (!this.batchQueue.length || !this.isConnected) return const batchData [...this.batchQueue] this.batchQueue [] this.send({ type: batch, messages: batchData, count: batchData.length, timestamp: Date.now() }).catch(err { console.error(批量发送失败:, err) // 失败后重新加入队列 this.batchQueue.unshift(...batchData) }) }内存管理长时间运行的WebSocket连接需要注意内存管理// 在WebSocketManager中添加清理方法 cleanup() { this.clearHeartbeat() this.clearReconnect() if (this.batchTimer) { clearTimeout(this.batchTimer) this.batchTimer null } this.messageQueue [] this.batchQueue [] this.messageHandlers.clear() } // 修改close方法 close() { this.cleanup() if (this.socketTask) { this.socketTask.close() this.socketTask null } this.isConnected false }6. 实际应用场景示例6.1 实时聊天应用消息发送封装class ChatService { constructor(socketManager) { this.socket socketManager this.messageQueue [] this.sequenceId 0 } sendTextMessage(roomId, text, callback) { const messageId this.generateMessageId() const message { type: chat, messageType: text, roomId, content: text, messageId, timestamp: Date.now() } this.socket.send(message) .then(() { callback({ success: true, messageId }) }) .catch(err { console.error(发送消息失败:, err) callback({ success: false, error: err.message }) }) } sendImageMessage(roomId, imageUrl, callback) { const messageId this.generateMessageId() const message { type: chat, messageType: image, roomId, imageUrl, messageId, timestamp: Date.now() } // 同上发送逻辑... } generateMessageId() { return ${Date.now()}-${this.sequenceId} } onMessageReceived(callback) { return this.socket.addMessageHandler((message) { if (message.type chat) { callback(message) } }) } joinRoom(roomId, userInfo) { return this.socket.send({ type: room, action: join, roomId, userInfo }) } leaveRoom(roomId) { return this.socket.send({ type: room, action: leave, roomId }) } }6.2 实时数据监控仪表盘数据处理封装class DataMonitor { constructor(socketManager) { this.socket socketManager this.dataCache new Map() this.subscriptions new Map() this.initHandlers() } initHandlers() { this.messageHandler this.socket.addMessageHandler((message) { if (message.type dataUpdate) { this.handleDataUpdate(message) } }) } handleDataUpdate(message) { const { metric, value, timestamp } message // 更新缓存 if (!this.dataCache.has(metric)) { this.dataCache.set(metric, []) } const dataPoints this.dataCache.get(metric) dataPoints.push({ value, timestamp }) // 保留最近100个数据点 if (dataPoints.length 100) { dataPoints.shift() } // 通知订阅者 if (this.subscriptions.has(metric)) { this.subscriptions.get(metric).forEach(callback { callback({ value, timestamp }) }) } } subscribe(metric, callback) { if (!this.subscriptions.has(metric)) { this.subscriptions.set(metric, new Set()) // 首次订阅时发送订阅请求 this.socket.send({ type: subscription, action: subscribe, metric }) } this.subscriptions.get(metric).add(callback) // 返回取消订阅函数 return () this.unsubscribe(metric, callback) } unsubscribe(metric, callback) { if (!this.subscriptions.has(metric)) return const callbacks this.subscriptions.get(metric) callbacks.delete(callback) if (callbacks.size 0) { this.subscriptions.delete(metric) // 没有订阅者时取消服务器推送 this.socket.send({ type: subscription, action: unsubscribe, metric }) } } getHistoricalData(metric, count 50) { if (!this.dataCache.has(metric)) return [] const allData this.dataCache.get(metric) return allData.slice(-count) } destroy() { this.messageHandler() this.dataCache.clear() this.subscriptions.clear() } }6.3 多人协作编辑系统操作同步实现class CollaborationService { constructor(socketManager, documentId) { this.socket socketManager this.documentId documentId this.operations [] this.remoteOperations [] this.initialize() } initialize() { // 加入协作房间 this.socket.send({ type: collaboration, action: join, documentId: this.documentId }) // 设置消息处理器 this.messageHandler this.socket.addMessageHandler((message) { if (message.type operation message.documentId this.documentId) { this.handleRemoteOperation(message) } }) } applyLocalOperation(operation) { // 生成操作ID const operationId this.generateOperationId() const timestamp Date.now() // 添加到本地操作历史 this.operations.push({ ...operation, operationId, timestamp }) // 发送到服务器 this.socket.send({ type: operation, documentId: this.documentId, operationId, operation, timestamp }) } handleRemoteOperation(message) { const { operation, operationId, timestamp } message // 检查是否已经处理过 if (this.remoteOperations.some(op op.operationId operationId)) { return } // 添加到远程操作历史 this.remoteOperations.push({ operation, operationId, timestamp }) // 应用操作到本地 this.applyOperationToDocument(operation) } generateOperationId() { return ${Date.now()}-${Math.random().toString(36).substr(2, 8)} } applyOperationToDocument(operation) { // 实际应用中这里会实现具体的文档操作逻辑 console.log(应用操作:, operation) } destroy() { this.messageHandler() // 离开协作房间 this.socket.send({ type: collaboration, action: leave, documentId: this.documentId }) } }7. 安全与生产环境实践7.1 安全加固措施认证与授权// 在WebSocketManager中添加认证逻辑 authenticate(token) { return this.send({ type: auth, token }).then(() { this.log(认证成功) this.isAuthenticated true }).catch(err { console.error(认证失败:, err) this.isAuthenticated false throw err }) } // 修改send方法添加认证检查 send(data) { if (this.requireAuth !this.isAuthenticated data.type ! auth) { return Promise.reject(new Error(未认证请先调用authenticate方法)) } // 原有发送逻辑... }消息验证// 添加消息验证方法 validateMessage(message) { if (!message || typeof message ! object) { throw new Error(无效消息格式) } // 检查必要字段 if (!message.type) { throw new Error(消息缺少type字段) } // 根据不同类型进行验证 switch (message.type) { case chat: if (!message.content || !message.roomId) { throw new Error(聊天消息缺少必要字段) } break case auth: if (!message.token) { throw new Error(认证消息缺少token) } break // 其他消息类型验证... } return true } // 修改handleMessage方法 handleMessage(res) { try { const message JSON.parse(res.data) this.validateMessage(message) this.messageHandlers.forEach(handler handler(message)) } catch (e) { console.error(消息处理错误:, e) // 发送错误响应 if (e.message.includes(无效消息格式)) { this.send({ type: error, code: INVALID_MESSAGE, message: e.message }) } } }7.2 生产环境部署建议服务器配置使用WSS(WebSocket Secure)替代WS配置合适的负载均衡器支持WebSocket设置合理的连接超时和最大连接数客户端最佳实践// 在应用启动时初始化WebSocket // app.vue export default { onLaunch() { // 生产环境配置 const socket new WebSocketManager(wss://production-server.com/ws, { heartbeatInterval: 25000, reconnectDelay: 5000, maxReconnectAttempts: Infinity, debug: false }) // 认证 socket.authenticate(uni.getStorageSync(authToken)) Vue.prototype.$socket socket }, onHide() { // 应用进入后台时暂停心跳 if (this.$socket) { this.$socket.pauseHeartbeat() } }, onShow() { // 应用回到前台时恢复心跳 if (this.$socket) { this.$socket.resumeHeartbeat() } } }监控与日志// 添加监控指标 class WebSocketMonitor { constructor(socketManager) { this.socket socketManager this.metrics { messagesSent: 0, messagesReceived: 0, connectionTime: 0, reconnects: 0, errors: 0 } this.setupMonitoring() } setupMonitoring() { // 消息计数 this.socket.addMessageHandler(() { this.metrics.messagesReceived }) // 原始send方法 const originalSend this.socket.send.bind(this.socket) // 包装send方法 this.socket.send async (data) { try { await originalSend(data) this.metrics.messagesSent return true } catch (err) { this.metrics.errors throw err } } // 连接时间统计 let connectTime this.socket.socketTask.onOpen(() { connectTime Date.now() }) this.socket.socketTask.onClose(() { if (connectTime) { this.metrics.connectionTime Date.now() - connectTime connectTime null } }) // 重连统计 this.socket.reconnect () { this.metrics.reconnects return this.socket.reconnect() } } getMetrics() { return { ...this.metrics, avgMessageRate: this.metrics.connectionTime 0 ? (this.metrics.messagesReceived / (this.metrics.connectionTime / 1000)).toFixed(2) : 0 } } }8. 常见问题与解决方案8.1 连接问题排查常见连接错误及解决方法错误类型可能原因解决方案连接超时网络问题/服务器未响应检查网络连接确认服务器地址正确增加连接超时时间SSL错误证书问题/域名不匹配确保证书有效且与域名匹配开发环境可临时禁用证书验证403禁止访问认证失败/跨域问题检查认证信息确认服务器CORS配置正确连接频繁断开心跳未正确配置/网络不稳定调整心跳间隔实现更健壮的重连逻辑8.2 性能问题优化消息频率控制// 在WebSocketManager中添加消息节流 constructor() { this.messageThrottle options.messageThrottle || 100 // 毫秒 this.lastSendTime 0 this.pendingSend null } // 修改send方法 send(data) { const now Date.now() const timeSinceLastSend now - this.lastSendTime if (timeSinceLastSend this.messageThrottle) { // 节流控制 if (this.pendingSend) { clearTimeout(this.pendingSend) } return new Promise((resolve, reject) { this.pendingSend setTimeout(() { this.doSend(data).then(resolve).catch(reject) }, this.messageThrottle - timeSinceLastSend) }) } return this.doSend(data) } // 实际发送方法 doSend(data) { this.lastSendTime Date.now() return new Promise((resolve, reject) { if (!this.isConnected) { reject(new Error(连接未就绪)) return } this.socketTask.send({ data: JSON.stringify(data), success: () resolve(), fail: (err) reject(err) }) }) }8.3 平台特定问题微信小程序注意事项必须在小程序后台配置合法WebSocket域名真机调试时必须使用wss协议注意小程序后台运行时的连接保持策略App端特殊处理// 检测网络状态变化 uni.onNetworkStatusChange((res) { if (res.isConnected) { if (!this.socket.isConnected) { this.socket.connect() } } else { this.socket.handleClose() } }) // 处理App进入后台 uni.onAppHide(() { this.socket.pauseHeartbeat() }) // 处理App回到前台 uni.onAppShow(() { this.socket.resumeHeartbeat() })9. 测试策略与质量保证9.1 单元测试方案使用Jest测试WebSocketManager核心功能// websocket.test.js import WebSocketManager from ./websocket describe(WebSocketManager, () { let socket beforeEach(() { socket new WebSocketManager(ws://test-server, { debug: true, heartbeatInterval: 100, reconnectDelay: 50 }) }) afterEach(() { socket.close() }) test(should initialize correctly, () { expect(socket.url).toBe(ws://test-server) expect(socket.isConnected).toBe(false) }) test(should handle connection and messages, done { const testMessage { type: test, data: hello } socket.addMessageHandler(message { expect(message).toEqual(testMessage) done() }) // 模拟WebSocket事件 socket.socketTask.onOpen() socket.socketTask.onMessage({ data: JSON.stringify(testMessage) }) }) test(should reconnect when connection lost, done { jest.useFakeTimers() socket.socketTask.onOpen() socket

更多文章