核心思路是统一管理WebSocket连接并用自定义事件分发消息:通过mitt等事件总线将onmessage封装为emit('message', data),组件按需订阅,解耦连接逻辑与业务,避免重复连接、状态混乱和内存泄漏。

Vue 自定义事件处理 WebSocket 消息的事件分发,核心思路是:不把 WebSocket 实例直接暴露给每个组件,而是统一管理连接,再通过 Vue 的自定义事件机制(如 this.$emit / this.$on 在 Vue 2,或 mitt / vue-global-events / provide/inject + emits 在 Vue 3)将消息广播出去,由需要的页面或组件按需订阅。
这种方式解耦了 WebSocket 连接逻辑与业务组件,避免重复创建、状态混乱、内存泄漏等问题。
一、全局统一管理 WebSocket 连接(推荐在 App.vue 或单独 WebSocketService.js 中)
// composables/useWebSocket.js(Vue 3 Composition API)
import { ref, onMounted, onBeforeUnmount } from 'vue'
import mitt from 'mitt'
const emitter = mitt()
const socket = ref(null)
const isConnected = ref(false)
export function useWebSocket(url) {
const connect = () => {
if (socket.value && socket.value.readyState === WebSocket.OPEN) return
socket.value = new WebSocket(url)
socket.value.onopen = () => {
isConnected.value = true
emitter.emit('open')
}
socket.value.onmessage = (event) => {
try {
const data = JSON.parse(event.data)
emitter.emit('message', data) // 统一分发消息事件
} catch (e) {
emitter.emit('message-raw', event.data) // 原始字符串兜底
}
}
socket.value.onerror = (err) => {
emitter.emit('error', err)
}
socket.value.onclose = () => {
isConnected.value = false
emitter.emit('close')
}
}
const send = (msg) => {
if (socket.value?.readyState === WebSocket.OPEN) {
socket.value.send(typeof msg === 'string' ? msg : JSON.stringify(msg))
}
}
const close = () => {
socket.value?.close()
}
onBeforeUnmount(close)
return {
isConnected,
connect,
send,
on: emitter.on,
off: emitter.off,
emit: emitter.emit
}
}
✅ 关键点:所有消息都走
emitter.emit('message', data),而不是让每个组件自己监听onmessage。
二、在任意组件中订阅 WebSocket 消息(按需监听)
<script setup>
import { onMounted } from 'vue'
import { useWebSocket } from '@/composables/useWebSocket'
const { on, off } = useWebSocket('wss://api.example.com/ws')
onMounted(() => {
// 订阅全局消息事件
on('message', (data) => {
if (data.type === 'NOTICE') {
console.log('收到系统通知:', data.content)
// 触发 UI 提示、更新 badge 等
}
})
on('open', () => {
console.log('WebSocket 已就绪')
})
on('error', (err) => {
console.warn('WebSocket 异常:', err)
})
})
// 组件卸载时自动清理(useWebSocket 内已注册 onBeforeUnmount),但也可手动 off
</script>
? 提示:你还可以封装成更语义化的事件名,比如
'user:online'、'order:update',服务端推送时带 type 字段,前端只监听自己关心的类型。
三、配合 Pinia / Vuex 实现跨组件状态同步(可选增强)
如果消息需要驱动全局状态(如未读数、在线用户列表),可在事件回调中提交 store:
创建 Flask + Vue.js 全栈项目,采用单端口架构、uv 依赖管理、结构化日志和生产级前端设计。当用户要求以下操作时触发:创建新的 Flask+Vue 项目、搭建 Web 应用、构建全栈 Python+Vue 应用、初始化 Flask Web 项目、设置单端口 Flask 前后端项目。触发词:新建项目、创建项目、搭建项目、Flask项目、Vue项目。
on('message', (data) => {
if (data.type === 'UNREAD_COUNT') {
useNoticeStore().setUnreadCount(data.count)
}
})
这样组件只需 useNoticeStore() 订阅 $state,无需感知 WebSocket。
四、注意事项与避坑
- 不要在多个组件里各自 new WebSocket:会导致多条连接、token 冲突、服务端限流。
-
务必关闭连接或清理监听器:尤其在路由切换、组件销毁时;
mitt的off要配对on,否则内存泄漏。 -
服务端推送结构建议标准化:
{ "type": "chat", "from": "user_123", "content": "hello", "timestamp": 1733759040 }前端按
type分发或过滤,比全量解析更清晰。 - 重连逻辑建议放在 service 层,而非组件内;可结合指数退避 + 最大重试次数。
不复杂但容易忽略。关键是把“连接”和“消费”分开,用事件当桥梁,而不是让每个页面都成为 WebSocket 的主人。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










