在 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>