Vue 中使用 WebSocket

在 Vue 中使用 WebSocket 核心是封装通用 WebSocket 工具类(保证复用性、统一管理连接状态),再在组件中调用,同时结合 Vue 生命周期管理连接的创建与销毁,避免内存泄漏和无效连接。以下是适配 Vue2的完整实现方案

方案一 使用Mixin

mixin文件 websocket-mixin.js

javascript 复制代码
// src/mixins/aiWebsocketMixin.js
export default {
  data() {
    return {
      ws: null,
      wsConnected: false,
      wsReconnectTimer: null,
      wsReconnectCount: 0,
      wsMaxReconnectAttempts: 5,
      wsReconnectInterval: 3000,

      // 心跳(可选,若后端不支持 ping/pong,可注释掉相关代码)
      wsHeartbeatTimer: null,
      wsHeartbeatInterval: 30000, // 30秒
      wsPongTimeoutId: null,
      wsPongTimeout: 10000,
    }
  },

  methods: {
    /**
     * 初始化 WebSocket 连接(传入完整 URL)
     * @param {string} wsUrl - 完整的 WebSocket 地址,含查询参数
     */
    initAiWebSocket(wsUrl) {
      this.closeAiWebSocket()

      try {
        this.ws = new WebSocket(wsUrl)

        this.ws.onopen = () => {
          console.log('AI WebSocket connected')
          this.wsConnected = true
          this.wsReconnectCount = 0
          this.$emit('ws-open')
          // this.startHeartbeat()
        }

        this.ws.onmessage = (event) => {
          let data
          try {
            data = JSON.parse(event.data)
          } catch (e) {
            console.warn('Non-JSON message:', event.data)
            return
          }

          // 如果后端支持 pong,可保留;否则可删除此判断
          if (data.type === 'pong') {
            this.handlePong()
            return
          }

          // 透传业务消息
          this.$emit('ws-message', data)
        }

        this.ws.onclose = () => {
          console.log('AI WebSocket disconnected')
          this.wsConnected = false
          this.$emit('ws-close')
          this.stopHeartbeat()
          this.scheduleReconnect(wsUrl)
        }

        this.ws.onerror = (error) => {
          console.error('AI WebSocket error', error)
          this.$emit('ws-error', error)
        }
      } catch (err) {
        console.error('Failed to create WebSocket', err)
      }
    },

    sendAiMessage(payload) {
      if (this.ws?.readyState === WebSocket.OPEN) {
        this.ws.send(JSON.stringify(payload))
      } else {
        console.warn('WebSocket not open. Message not sent:', payload)
      }
    },

    // ------ 心跳(按需启用)------
    startHeartbeat() {
      this.stopHeartbeat()
      // 如果后端不支持 ping/pong,可注释掉 send 部分或整个定时器
      this.wsHeartbeatTimer = setInterval(() => {
        if (this.ws?.readyState === WebSocket.OPEN) {
          // 可选:发送心跳包
          // this.ws.send(JSON.stringify({ type: 'ping' }))
        }
      }, this.wsHeartbeatInterval)
    },

    handlePong() {
      if (this.wsPongTimeoutId) {
        clearTimeout(this.wsPongTimeoutId)
        this.wsPongTimeoutId = null
      }
    },

    stopHeartbeat() {
      if (this.wsHeartbeatTimer) {
        clearInterval(this.wsHeartbeatTimer)
        this.wsHeartbeatTimer = null
      }
      if (this.wsPongTimeoutId) {
        clearTimeout(this.wsPongTimeoutId)
        this.wsPongTimeoutId = null
      }
    },

    // ------ 重连 ------
    scheduleReconnect(wsUrl) {
      if (this.wsReconnectCount < this.wsMaxReconnectAttempts) {
        this.wsReconnectCount++
        console.log(`Reconnecting... (${this.wsReconnectCount}/${this.wsMaxReconnectAttempts})`)
        this.wsReconnectTimer = setTimeout(() => {
          this.initAiWebSocket(wsUrl)
        }, this.wsReconnectInterval)
      }
    },

    // ------ 关闭 ------
    closeAiWebSocket() {
      this.stopHeartbeat()
      if (this.wsReconnectTimer) {
        clearTimeout(this.wsReconnectTimer)
        this.wsReconnectTimer = null
      }
      if (this.ws) {
        this.ws.close()
        this.ws = null
        this.wsConnected = false
      }
    }
  },

  beforeDestroy() {
    this.closeAiWebSocket()
  }
}

使用

javascript 复制代码
<template>
  <div>
    <button @click="handleSend">发送</button>
    {{message}}
  </div>
</template>

<script>
import aiWebsocketMixin from '@/mixins/aiWebsocketMixin'

export default {
  name: 'Websocket',
  mixins: [aiWebsocketMixin],
  data() {
    return {
      message: ''
    }
  },
  mounted() {
    const wsUrl = 'ws://127.0.0.1:8000/chat'
    this.initAiWebSocket(wsUrl)
  },
  created() {
    this.$on('ws-message', this.handleMessage)
  },
  methods: {
    // ------------------ 发送消息 ------------------
    handleSend() {
      const params = {
        message: '你好'
      }
      this.sendAiMessage(params)
    },
    // ------------------ 消息处理 ------------------
    handleMessage(message) {
      this.message = message
    },
  },
  beforeDestroy() {
    this.$off('ws-message', this.handleMessage)
  }
}
</script>

相关推荐
AI袋鼠帝6 小时前
火爆全网的Seedance2.0 十万人排队,我2分钟就用上了
前端
IT_陈寒6 小时前
React Hooks闭包陷阱:你以为的state可能早就过期了
前端·人工智能·后端
Jenlybein6 小时前
快速了解熟悉 Vite ,即刻上手使用
前端·javascript·vite
小码哥_常6 小时前
安卓开发避坑指南:全局异常捕获与优雅处理实战
前端
lihaozecq6 小时前
我用 1 天的时间 vibe coding 了一个多人德州扑克游戏
前端·react.js·ai编程
momo061176 小时前
AI Skill是什么?
前端·ai编程
言萧凡_CookieBoty6 小时前
用 AI 搞定用户系统:Superpowers 工程化开发教程
前端·ai编程
小小小小宇6 小时前
Go 语言协程
前端
牛奶6 小时前
5MB vs 4KB vs 无限大:浏览器存储谁更强?
前端·浏览器·indexeddb
牛奶6 小时前
setTimeout设为0就马上执行?JS异步背后的秘密
前端·性能优化·promise