UniApp ConnectSocket连接websocket - 详解

useSocket WebSocket 使用说明

项目地址

概述

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)
参数名类型默认值说明
urlstring''WebSocket 服务器地址
maxReconnectCountnumber5最大重连次数
reconnectIntervalnumber3000重连间隔(毫秒)
autoConnectbooleantrue是否自动连接
heartBeatIntervalnumber5000心跳间隔(毫秒)
onOpenfunction-连接成功回调
onMessagefunction-接收消息回调
onErrorfunction-连接错误回调
onClosefunction-连接关闭回调
onMaxReconnectfunction-达到最大重连次数回调
返回值
属性名类型说明
isConnectedref(boolean)连接状态
reconnectCountref(number)当前重连次数
messagesref(Array)消息列表
connectfunction手动连接方法
disconnectfunction断开连接方法
sendfunction发送消息方法
clearMessagesfunction清空消息方法

方法详解

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">
      <input
      v-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">
    <view
    v-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;
// 连接 WebSocket
const 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 解决方案,具有以下优势:

  1. 开箱即用 - 简单的 API 设计,快速上手
  2. 功能完整 - 包含连接管理、重连、心跳等完整功能
  3. 错误处理 - 完善的错误处理和回调机制
  4. 性能优化 - 自动清理和生命周期管理
  5. 易于扩展 - 基于 Vue 3 Composition API,易于扩展和定制

通过本说明文档,您应该能够快速在项目中使用 useSocket 实现 WebSocket 功能。如有任何问题,请参考示例代码或查看源码实现。
在这里插入图片描述

---

技能提升

想要更系统地学习?推荐以下课程:

posted on 2025-09-26 21:08  slgkaifa  阅读(890)  评论(0)    收藏  举报

导航