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>

相关推荐
无名的小白2 小时前
openclaw使用nginx反代部署过程 与disconnected (1008): pairing required解决
java·前端·nginx
2601_949857432 小时前
Flutter for OpenHarmony Web开发助手App实战:文本统计
前端·flutter
光影少年2 小时前
智能体UI ux pro max
前端·ui·ux
半梅芒果干2 小时前
vue3 实现无缝循环滚动
前端·javascript·vue.js
qq_419854052 小时前
锚点跳转及鼠标滚动与锚点高亮联动
前端
冰敷逆向2 小时前
京东h5st纯算分析
java·前端·javascript·爬虫·安全·web
Laurence3 小时前
从零到一构建 C++ 项目(IDE / 命令行双轨实现)
前端·c++·ide
雯0609~3 小时前
hiprint-官网vue完整版本+实现客户端配置+可实现直接打印(在html版本增加了条形码、二维码拖拽等)
前端·javascript·vue.js
GISer_Jing3 小时前
构建高性能Markdown引擎开发计划
前端·aigc·ai编程