本文详细介绍如何在 Vue 3 中封装一个功能完善的 WebSocket Hook,支持自动重连、心跳检测等核心功能。

概述

在现代 Web 应用中,实时通信变得越来越重要。WebSocket 作为一种全双工通信协议,能够实现客户端与服务器之间的持久连接。本文将介绍如何在 Vue 3 中封装一个功能完善的 WebSocket Hook,支持自动重连、心跳检测等核心功能。

核心功能

  1. 自动连接 - 创建 Hook 时自动建立连接

  2. 自动重连 - 连接断开后自动尝试重新连接,可配置重试次数和间隔

  3. 心跳检测 - 定期发送心跳包保持连接活跃

  4. 状态管理 - 跟踪连接状态(未连接/连接中/已连接)

  5. 消息发送 - 提供统一的消息发送方法

  6. 自动清理 - 组件卸载时自动关闭连接和清理资源完整代码实现

完整代码实现

import { ref, onUnmounted } from "vue";

export default (url, options = {}) => {
  // 默认配置
  const {
    reconnectLimit = 3, // 重连限制次 number | null = 无限重连
    reconnectInterval = 3000, // 重连间隔 ms
    heartbeatInterval = 30000, // 心跳间隔 ms
    onOpen, // 连接成功时的回调函数
    onError, // 连接出错时的回调函数
    onMessage, // 收到消息时的回调函数
    onClose, // 连接关闭时的回调函数
  } = options;
  
  let status = "disconnected"; // 'disconnected: 未连接' | 'connecting: 正在连接' | 'connected: 已连接'
  let socket = null; // socket 对象
  let reconnectCount = 0; // 重新连接次数
  let reconnectTimer = null; // 重新连接计时器
  let heartbeatTimer = null; // 心跳计时器

  // 初始化 WebSocket
  const initWebSocket = () => {
    if (socket) socket.close();
    status = "connecting";
    socket = new WebSocket(url);
    
    socket.onopen = (event) => {
      status = "connected";
      reconnectCount = 0;
      startHeartbeat();
      onOpen?.(event);
    };

    socket.onmessage = (event) => {
      if (event.data === "pong" || event.data === "ping") return; // 忽略心跳响应
      onMessage?.(event.data);
    };

    socket.onerror = (error) => {
      onError?.(error);
      handleDisconnect();
    };

    socket.onclose = (event) => {
      onClose?.(event);
      handleDisconnect();
    };
  };

  // 处理断开连接
  const handleDisconnect = () => {
    status = "disconnected";
    stopHeartbeat();

    if (reconnectLimit === null || reconnectCount < reconnectLimit) {
      reconnectCount++;
      reconnectTimer = setTimeout(initWebSocket, reconnectInterval);
    }
  };

  // 启动心跳
  const startHeartbeat = () => {
    stopHeartbeat();
    heartbeatTimer = setInterval(() => {
      if (status === "connected" && socket) {
        socket.send("ping");
      }
    }, heartbeatInterval);
  };

  // 停止心跳
  const stopHeartbeat = () => {
    if (heartbeatTimer) {
      clearInterval(heartbeatTimer);
      heartbeatTimer = null;
    }
  };

  // 连接 WebSocket
  const connect = () => {
    if (status !== "disconnected") return;
    reconnectCount = 0;
    initWebSocket();
  };

  // 断开 WebSocket
  const disconnect = () => {
    if (status !== "connected") return;
    socket?.close(1000, "User disconnected");
  };

  // 发送消息
  const send = (message) => {
    if (status !== "connected" || !socket) return;
    socket.send(
      typeof message === "object" ? JSON.stringify(message) : message
    );
  };

  // 组件卸载时清理
  onUnmounted(() => {
    disconnect();
    if (reconnectTimer) clearTimeout(reconnectTimer);
    stopHeartbeat();
  });

  // 自动连接
  connect();

  return {
    connect,
    disconnect,
    send,
    getStatus: () => status,
    getReconnectCount: () => reconnectCount
  };
};

使用方法

1.基本使用

import useSocket from "@/hooks/websocket";

// 在组件中使用
export default {
  setup() {
    const wsUrl = "wss://your-websocket-server.com";
    
    const { send, disconnect, connect } = useSocket(wsUrl, {
      onOpen: () => console.log("WebSocket 连接成功"),
      onClose: () => console.log("WebSocket 断开连接"),
      onError: (err) => console.log("WebSocket 连接错误", err),
      onMessage: (data) => {
        console.log("收到消息:", data);
        // 处理收到的消息
      },
    });

    // 发送消息
    const sendMessage = () => {
      send({ type: "message", content: "Hello WebSocket!" });
    };

    return {
      sendMessage,
      disconnect,
      connect
    };
  }
};

2.在 Composition API 中的使用

import { ref, onMounted } from "vue";
import useSocket from "@/hooks/websocket";

export default {
  setup() {
    const messages = ref([]);
    const wsUrl = "wss://your-websocket-server.com";
    
    // 使用 WebSocket Hook
    const { send } = useSocket(wsUrl, {
      onOpen: () => {
        console.log("WebSocket 连接成功");
        // 连接成功后发送认证消息等
        send({ type: "auth", token: "your-token" });
      },
      onClose: () => console.log("WebSocket 断开连接"),
      onError: (err) => console.error("WebSocket 连接错误", err),
      onMessage: (data) => {
        try {
          const message = JSON.parse(data);
          messages.value.push(message);
        } catch (e) {
          console.error("消息解析错误:", e);
        }
      },
    });

    // 发送消息
    const sendMessage = (content) => {
      send({ type: "chat", content });
    };

    return {
      messages,
      sendMessage
    };
  }
};

配置选项

参数名 类型 默认值 描述
reconnectLimit number 3 重连限制次数,设置为 null 表示无限重连
reconnectInterval number 3000 重连间隔时间(毫秒)
heartbeatInterval number 30000 心跳间隔时间(毫秒)
onOpen function null 连接成功时的回调函数
onError function null 连接出错时的回调函数
onMessage function null 收到消息时的回调函数
onClose function null 连接关闭时的回调函数

注意事项

  1. 心跳协议:确保你的 WebSocket 服务器能正确处理 "ping" 消息并可能返回 "pong" 响应

  2. 重连策略:根据实际需求调整重连次数和间隔

  3. 错误处理:合理处理连接错误和消息解析错误

  4. 资源清理:组件卸载时确保正确关闭连接和清理定时器

总结

本文介绍的 WebSocket Hook 提供了一个强大且灵活的解决方案,用于在 Vue 3 应用中管理 WebSocket 连接。通过自动重连、心跳检测等功能的封装,大大简化了 WebSocket 的使用复杂度,提高了开发效率和应用稳定性。

你可以根据实际项目需求进一步扩展这个 Hook,比如添加消息队列、连接状态变更事件、重连策略自定义等功能。


💰【我的自研工具推荐】轻松网购返利,技术人的省钱助手

✅ 自用省钱,分享有奖
支持主流电商平台 | 返利透明可提现

👇 微信扫码,立即体验

说明:本工具为我个人独立开发,若您有技术合作或使用疑问,欢迎在博客私信交流。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐