UniApp ConnectSocket连接websocket

useSocket WebSocket 使用说明

项目地址

概述

useSocket 是一个基于 Vue 3 Composition API 的 WebSocket 封装 Hook,专为 UniApp 项目设计。它提供了完整的 WebSocket 连接管理、自动重连、心跳检测等功能,让您能够轻松地在 UniApp 项目中使用 WebSocket 进行实时通信。

功能特性

  • 自动连接管理 - 支持自动连接和手动连接
  • 自动重连机制 - 连接断开时自动重连,可配置重连次数和间隔
  • 心跳检测 - 定期发送心跳包保持连接活跃
  • 消息管理 - 自动解析和存储接收到的消息
  • 状态监控 - 实时监控连接状态和重连次数
  • 生命周期管理 - 组件卸载时自动断开连接
  • 错误处理 - 完善的错误处理和回调机制

安装和导入

1. 克隆项目

首先克隆相关项目到本地:

bash 复制代码
# 克隆 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. 导入方式

javascript 复制代码
import {useSocket} from '@/hooks/useSocket';

基本使用

1. 最简单的使用方式

javascript 复制代码
<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. 完整配置示例

javascript 复制代码
<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 连接。

javascript 复制代码
const {connect} = useSocket({url: 'ws://localhost:8080/ws'});
connect();
disconnect()

手动断开 WebSocket 连接。

javascript 复制代码
const {disconnect} = useSocket({url: 'ws://localhost:8080/ws'});
disconnect();
send(data)

发送消息到服务器。

参数:

  • data (string | object): 要发送的数据,可以是字符串或对象

返回值: Promise

javascript 复制代码
// 发送字符串
await send('Hello World');

// 发送对象
await send({
    type: 'message',
    content: 'Hello World',
    timestamp: Date.now(),
});
clearMessages()

清空消息列表。

javascript 复制代码
const {clearMessages} = useSocket({url: 'ws://localhost:8080/ws'});
clearMessages();

实际项目使用示例

聊天应用示例

javascript 复制代码
<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>

实时通知示例

javascript 复制代码
<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 源码

javascript 复制代码
// 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 功能。如有任何问题,请参考示例代码或查看源码实现。

相关推荐
FreeBuf_2 小时前
Zloader木马再次升级:通过DNS隧道和WebSocket C2实现更隐蔽的攻击
websocket·网络协议·php
Nan_Shu_6142 小时前
学习:uniapp全栈微信小程序vue3后台(28)
前端·学习·微信小程序·小程序·uni-app
whltaoin3 小时前
SpringCloud 项目阶段十:kafka实现双端信息同步以及ElasticSearch容器搭建示例
elasticsearch·spring cloud·kafka
—Qeyser3 小时前
Laravel + UniApp AES加密/解密
前端·uni-app·laravel
游戏开发爱好者83 小时前
Nginx HTTPS 深入实战 配置、性能与排查全流程(Nginx https
运维·nginx·ios·小程序·https·uni-app·iphone
Elasticsearch4 小时前
Elastic 获得 2025 年最佳 AI 辅助支持应用奖
elasticsearch
游戏开发爱好者84 小时前
TCP 抓包分析:tcp抓包工具、 iOS/HTTPS 流量解析全流程
网络协议·tcp/ip·ios·小程序·https·uni-app·iphone
辛宝Otto_WebWorker5 小时前
自力更生!uniapp 使用鸿蒙 UTS 使用三方依赖、本地依赖
uni-app·harmonyos
Q_Q5110082855 小时前
python+uniapp基于微信小程序美食点餐系统
spring boot·python·微信小程序·django·flask·uni-app·node.js