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

相关推荐
Q_Q51100828520 小时前
python+uniapp基于微信小程序的旅游信息系统
spring boot·python·微信小程序·django·flask·uni-app·node.js
失散1321 小时前
分布式专题——43 ElasticSearch概述
java·分布式·elasticsearch·架构
2501_916007471 天前
iOS 混淆工具链实战,多工具组合完成 IPA 混淆与加固(iOS混淆|IPA加固|无源码混淆|App 防反编译)
android·ios·小程序·https·uni-app·iphone·webview
Arva .1 天前
WebSocket实现网站点赞通知
网络·websocket·网络协议
游戏开发爱好者81 天前
FTP 抓包分析实战,命令、被动主动模式要点、FTPS 与 SFTP 区别及真机取证流程
运维·服务器·网络·ios·小程序·uni-app·iphone
望获linux1 天前
【实时Linux实战系列】实时 Linux 的自动化基准测试框架
java·大数据·linux·运维·网络·elasticsearch·搜索引擎
风清再凯1 天前
01-ELK安装ES,ES-head
大数据·elk·elasticsearch
2501_915909061 天前
iOS 26 文件管理实战,多工具组合下的 App 数据访问与系统日志调试方案
android·ios·小程序·https·uni-app·iphone·webview
火星数据-Tina1 天前
LOL实时数据推送技术揭秘:WebSocket在电竞中的应用
网络·websocket·网络协议