UniApp ConnectSocket连接websocket
useSocket WebSocket 使用说明
项目地址
- Node.js 服务端项目: https://gitee.com/node-server_yarn/node_test_learn.git
- UniApp 客户端项目: https://gitee.com/uniapp_yarn/uniapp_socket_task.git
概述
useSocket
是一个基于 Vue 3 Composition API 的 WebSocket 封装 Hook,专为 UniApp 项目设计。它提供了完整的 WebSocket 连接管理、自动重连、心跳检测等功能,让您能够轻松地在 UniApp 项目中使用 WebSocket 进行实时通信。
功能特性
- ✅ 自动连接管理 - 支持自动连接和手动连接
- ✅ 自动重连机制 - 连接断开时自动重连,可配置重连次数和间隔
- ✅ 心跳检测 - 定期发送心跳包保持连接活跃
- ✅ 消息管理 - 自动解析和存储接收到的消息
- ✅ 状态监控 - 实时监控连接状态和重连次数
- ✅ 生命周期管理 - 组件卸载时自动断开连接
- ✅ 错误处理 - 完善的错误处理和回调机制
安装和导入
1. 克隆项目
首先克隆相关项目到本地:
# 克隆 Node.js 服务端项目
git clone https://gitee.com/node-server_yarn/node_test_learn.git# 克隆 UniApp 客户端项目
git clone https://gitee.com/uniapp_yarn/uniapp_socket_task.git
2. 文件位置
将 useSocket.js
文件放置在项目的 src/hooks/
目录下。
3. 导入方式
import {useSocket} from '@/hooks/useSocket';
基本使用
1. 最简单的使用方式
<template><view><text>连接状态: {{ isConnected ? '已连接' : '未连接' }}</text><button @click="sendMessage">发送消息</button></view>
</template><script setup>
import { useSocket } from '@/hooks/useSocket';// 创建 WebSocket 连接
const { isConnected, send, messages } = useSocket({url: 'ws://localhost:8080/ws',onMessage: (message) => {console.log('收到消息:', message);}
});// 发送消息
const sendMessage = () => {send({type: 'text',content: 'Hello WebSocket!'});
};
</script>
2. 完整配置示例
<template><view class="chat-container"><!-- 连接状态显示 --><view class="status-bar"><text>状态: {{ isConnected ? '已连接' : '未连接' }}</text><text v-if="reconnectCount > 0">重连次数: {{ reconnectCount }}</text></view><!-- 消息列表 --><scroll-view class="message-list" scroll-y><view v-for="message in messages" :key="message.timestamp" class="message-item"><text>{{ message.content }}</text></view></scroll-view><!-- 输入框 --><view class="input-area"><input v-model="inputText" placeholder="输入消息..." @confirm="handleSend" /><button @click="handleSend">发送</button></view></view>
</template><script setup>
import { ref } from 'vue';
import { useSocket } from '@/hooks/useSocket';const inputText = ref('');// 创建 WebSocket 连接
const {isConnected,reconnectCount,messages,connect,disconnect,send,clearMessages
} = useSocket({// WebSocket 服务器地址url: 'ws://localhost:8080/ws',// 最大重连次数maxReconnectCount: 5,// 重连间隔(毫秒)reconnectInterval: 3000,// 是否自动连接autoConnect: true,// 心跳间隔(毫秒)heartBeatInterval: 5000,// 连接成功回调onOpen: () => {console.log('WebSocket 连接成功');uni.showToast({title: '连接成功',icon: 'success'});},// 接收消息回调onMessage: (message) => {console.log('收到消息:', message);// 可以在这里处理不同类型的消息if (message.type === 'notification') {uni.showToast({title: message.content,icon: 'none'});}},// 连接错误回调onError: (error) => {console.error('WebSocket 错误:', error);uni.showToast({title: '连接错误',icon: 'error'});},// 连接关闭回调onClose: (event) => {console.log('WebSocket 连接关闭:', event);},// 达到最大重连次数回调onMaxReconnect: () => {console.log('已达到最大重连次数');uni.showModal({title: '连接失败',content: '无法连接到服务器,请检查网络连接',showCancel: false});}
});// 发送消息
const handleSend = async () => {if (!inputText.value.trim()) return;try {await send({type: 'text',content: inputText.value.trim(),timestamp: Date.now()});inputText.value = '';console.log('消息发送成功');} catch (error) {console.error('发送失败:', error);uni.showToast({title: '发送失败',icon: 'error'});}
};// 手动连接
const handleConnect = () => {connect();
};// 手动断开连接
const handleDisconnect = () => {disconnect();
};// 清空消息
const handleClearMessages = () => {clearMessages();
};
</script>
API 参考
useSocket(options)
参数 (options)
参数名 | 类型 | 默认值 | 说明 |
---|---|---|---|
url | string | '' | WebSocket 服务器地址 |
maxReconnectCount | number | 5 | 最大重连次数 |
reconnectInterval | number | 3000 | 重连间隔(毫秒) |
autoConnect | boolean | true | 是否自动连接 |
heartBeatInterval | number | 5000 | 心跳间隔(毫秒) |
onOpen | function | - | 连接成功回调 |
onMessage | function | - | 接收消息回调 |
onError | function | - | 连接错误回调 |
onClose | function | - | 连接关闭回调 |
onMaxReconnect | function | - | 达到最大重连次数回调 |
返回值
属性名 | 类型 | 说明 |
---|---|---|
isConnected | ref(boolean) | 连接状态 |
reconnectCount | ref(number) | 当前重连次数 |
messages | ref(Array) | 消息列表 |
connect | function | 手动连接方法 |
disconnect | function | 断开连接方法 |
send | function | 发送消息方法 |
clearMessages | function | 清空消息方法 |
方法详解
connect()
手动建立 WebSocket 连接。
const {connect} = useSocket({url: 'ws://localhost:8080/ws'});
connect();
disconnect()
手动断开 WebSocket 连接。
const {disconnect} = useSocket({url: 'ws://localhost:8080/ws'});
disconnect();
send(data)
发送消息到服务器。
参数:
data
(string | object): 要发送的数据,可以是字符串或对象
返回值: Promise
// 发送字符串
await send('Hello World');// 发送对象
await send({type: 'message',content: 'Hello World',timestamp: Date.now(),
});
clearMessages()
清空消息列表。
const {clearMessages} = useSocket({url: 'ws://localhost:8080/ws'});
clearMessages();
实际项目使用示例
聊天应用示例
<template><view class="chat-page"><!-- 聊天消息列表 --><scroll-view class="message-list" scroll-y :scroll-top="scrollTop"><view v-for="message in messages" :key="message.id" class="message-item"><view class="message-avatar"><image :src="message.avatar" mode="aspectFill" /></view><view class="message-content"><text>{{ message.content }}</text><text class="message-time">{{ message.timestamp }}</text></view></view></scroll-view><!-- 输入区域 --><view class="input-area"><inputv-model="inputText"placeholder="输入消息..."@confirm="handleSend":disabled="!isConnected"/><button@click="handleSend":disabled="!isConnected || !inputText.trim()">发送</button></view></view>
</template><script setup>
import { ref, onMounted, nextTick } from 'vue';
import { useSocket } from '@/hooks/useSocket';const inputText = ref('');
const scrollTop = ref(0);// 创建 WebSocket 连接
const { isConnected, messages, send } = useSocket({url: 'ws://localhost:8080/chat',onMessage: (message) => {console.log('收到聊天消息:', message);// 自动滚动到底部nextTick(() => {scrollToBottom();});},onOpen: () => {console.log('聊天连接已建立');},onError: (error) => {console.error('聊天连接错误:', error);uni.showToast({title: '连接失败',icon: 'error'});}
});// 发送消息
const handleSend = async () => {if (!inputText.value.trim() || !isConnected.value) return;const message = {type: 'text',content: inputText.value.trim(),timestamp: new Date().toLocaleTimeString(),id: Date.now()};try {await send(message);inputText.value = '';} catch (error) {console.error('发送消息失败:', error);uni.showToast({title: '发送失败',icon: 'error'});}
};// 滚动到底部
const scrollToBottom = () => {const query = uni.createSelectorQuery();query.select('.message-list').boundingClientRect();query.exec((res) => {if (res[0]) {scrollTop.value = res[0].height;}});
};onMounted(() => {// 页面加载时自动连接console.log('聊天页面已加载');
});
</script>
实时通知示例
<template><view class="notification-page"><view class="status-indicator" :class="{ connected: isConnected }"><text>{{ isConnected ? '在线' : '离线' }}</text></view><view class="notification-list"><viewv-for="notification in messages":key="notification.id"class="notification-item"><text>{{ notification.content }}</text><text class="notification-time">{{ notification.timestamp }}</text></view></view></view>
</template><script setup>
import { useSocket } from '@/hooks/useSocket';// 创建通知 WebSocket 连接
const { isConnected, messages } = useSocket({url: 'ws://localhost:8080/notifications',onMessage: (notification) => {console.log('收到通知:', notification);// 显示系统通知uni.showToast({title: notification.content,icon: 'none',duration: 3000});},onOpen: () => {console.log('通知连接已建立');}
});
</script>
注意事项
1. 网络环境
- 确保 WebSocket 服务器地址正确且可访问
- 在真机调试时,需要使用实际的 IP 地址而非 localhost
2. 生命周期管理
- Hook 会在组件卸载时自动断开连接
- 如需在多个组件间共享连接,建议使用全局状态管理
3. 错误处理
- 建议为所有回调函数提供错误处理逻辑
- 网络异常时会有自动重连机制,但达到最大重连次数后需要手动处理
4. 性能优化
- 消息列表会持续增长,建议在适当时机调用
clearMessages()
清空 - 心跳间隔不宜过短,避免频繁的网络请求
5. 平台兼容性
- 本 Hook 基于 UniApp 的
uni.connectSocket
API - 支持所有 UniApp 支持的平台(H5、小程序、App)
完整源码
useSocket.js 源码
// composables/useWebSocket.js
import {ref, reactive, onUnmounted} from 'vue';export function useSocket(options = {}) {// 状态const isConnected = ref(false);const reconnectCount = ref(0);const messages = ref([]);const socketTask = ref(null);// 配置const config = reactive({url: options.url || '',maxReconnectCount: options.maxReconnectCount || 5, // 最大重连次数reconnectInterval: options.reconnectInterval || 3000, // 重连间隔autoConnect: options.autoConnect !== false, // 自动连接heartBeatInterval: options.heartBeatInterval || 5000, // 心跳间隔...options,});// 心跳计时器let heartBeatTimer = null;// 连接 WebSocketconst connect = () => {if (socketTask.value) {disconnect();}// console.log('正在连接 WebSocket...');socketTask.value = uni.connectSocket({url: config.url,header: {'content-type': 'application/json',},success: () => {console.log('[WebSocket] 连接创建成功');},fail: (err) => {console.error('[WebSocket] 连接创建失败', err);options.onError?.(err);handleReconnect();},});bindEvents();};// 绑定事件const bindEvents = () => {if (!socketTask.value) return;socketTask.value.onOpen(() => {console.log('[WebSocket] 连接已打开');isConnected.value = true;reconnectCount.value = 0;options.onOpen?.();startHeartBeat();});socketTask.value.onMessage((res) => {const message = parseMessage(res.data);options.onMessage?.(message);messages.value.push(message);});socketTask.value.onError((err) => {console.error('[WebSocket] 连接错误:', err);isConnected.value = false;options.onError?.(err);handleReconnect();});socketTask.value.onClose((res) => {console.log('[WebSocket] 连接已关闭', res);isConnected.value = false;options.onClose?.(res);stopHeartBeat();});};// 发送消息const send = (data) => {if (!isConnected.value || !socketTask.value) {throw new Error('WebSocket 未连接');}const message = typeof data === 'string' ? data : JSON.stringify(data);return new Promise((resolve, reject) => {socketTask.value.send({data: message,success: () => {console.log('消息发送成功');resolve();},fail: (err) => {console.error('消息发送失败', err);reject(err);},});});};// 断开连接const disconnect = () => {if (socketTask.value && socketTask.value.readyState === 1) {socketTask.value.close();socketTask.value = null;}isConnected.value = false;stopHeartBeat();};// 重连机制const handleReconnect = () => {if (reconnectCount.value >= config.maxReconnectCount) {console.log('[WebSocket] 已达到最大重连次数');options.onMaxReconnect?.();return;}reconnectCount.value++;console.log(`[WebSocket] 尝试第 ${reconnectCount.value} 次重连...`);setTimeout(() => {connect();}, config.reconnectInterval);};// 解析消息const parseMessage = (data) => {try {return JSON.parse(data);} catch {return {type: 'text',content: data,timestamp: Date.now(),};}};// 心跳检测const startHeartBeat = () => {if (!config.heartBeatInterval) return;stopHeartBeat();heartBeatTimer = setInterval(() => {if (isConnected.value) {send({type: 'ping',timestamp: Date.now(),}).catch((err) => {console.error('[WebSocket] 心跳发送失败', err);});}}, config.heartBeatInterval);};const stopHeartBeat = () => {if (heartBeatTimer) {clearInterval(heartBeatTimer);heartBeatTimer = null;}};// 清空消息const clearMessages = () => {messages.value = [];};// 自动连接if (config.autoConnect && config.url) {connect();}// 组件卸载时清理onUnmounted(() => {disconnect();});return {// 状态isConnected,reconnectCount,messages,// 方法connect,disconnect,send,clearMessages,};
}
总结
useSocket
Hook 为 UniApp 项目提供了完整的 WebSocket 解决方案,具有以下优势:
- 开箱即用 - 简单的 API 设计,快速上手
- 功能完整 - 包含连接管理、重连、心跳等完整功能
- 错误处理 - 完善的错误处理和回调机制
- 性能优化 - 自动清理和生命周期管理
- 易于扩展 - 基于 Vue 3 Composition API,易于扩展和定制
通过本说明文档,您应该能够快速在项目中使用 useSocket
实现 WebSocket 功能。如有任何问题,请参考示例代码或查看源码实现。