如何将基于$on、$emit的Vue2事件总线迁移至Vue3?
Vue 3 版本全局事件总线迁移实现
迁移核心前提
Vue 3 正式移除了组件实例上的 $on、$off、$once 系列事件方法,原方案中靠新建Vue实例充当事件载体的逻辑无法直接在Vue 3环境运行。迁移的核心目标是保持原有上层API完全兼容,尽可能减少已有业务代码的改动量。
无第三方依赖的原生实现
这种实现和原代码的轻量特性完全匹配,不需要引入任何外部包,对外暴露的方法名、传参逻辑和原代码1:1对齐:
window.Eventing = new (class { constructor() { // 用空对象存储所有事件对应的回调集合 this.eventCallbackMap = Object.create(null); } // 对齐原fire方法,支持任意数量的参数透传 fire(eventName, ...payload) { const targetCallbacks = this.eventCallbackMap[eventName]; // 事件没有绑定回调时直接返回,无额外性能损耗 if (!targetCallbacks?.length) return; // 依次触发所有绑定的回调,透传所有参数 targetCallbacks.forEach(cb => cb(...payload)); } // 对齐原listen方法的事件注册逻辑 listen(eventName, callback) { if (!this.eventCallbackMap[eventName]) { this.eventCallbackMap[eventName] = []; } this.eventCallbackMap[eventName].push(callback); } // 补充原实现缺失的事件取消方法,解决长期运行的内存泄漏问题 off(eventName, callback) { if (!this.eventCallbackMap[eventName]) return; this.eventCallbackMap[eventName] = this.eventCallbackMap[eventName].filter(cb => cb !== callback); // 事件没有剩余回调时及时清空引用,减少内存占用 if (this.eventCallbackMap[eventName].length === 0) { delete this.eventCallbackMap[eventName]; } } })();
这个实现的适配细节:
- 原代码中硬编码两个参数的写法存在扩展限制,迁移后的实现用剩余参数语法接收透传值,不仅完全兼容原双参数的使用场景,还支持任意数量的参数传递,和原代码的行为一致。
- 所有已有业务侧的调用代码(比如
Eventing.fire('userLogin', userInfo, extraData)、Eventing.listen('userLogin', (info, extra) => {}))不需要做任何修改,可直接在Vue 3环境运行。 - 补充了主动取消事件的能力,解决原实现中组件销毁后回调仍残留的常见问题。
基于成熟事件库的实现
如果团队已经在项目中使用轻量事件工具,只需要做一层简单封装即可对齐原API:
// 引入轻量事件库mitt(Vue官方文档推荐的原EventBus替代方案) import mitt from 'mitt'; const emitter = mitt(); window.Eventing = new (class { fire(eventName, ...payload) { // 注意:mitt原生仅支持单参数透传,这里需要把多参数打包后传递 emitter.emit(eventName, payload); } listen(eventName, callback) { // 监听时把打包的参数解包,传给业务回调,保持和原实现的传参一致 emitter.on(eventName, (payload) => callback(...payload)); } off(eventName, callback) { emitter.off(eventName, callback); } })();
迁移注意事项
- 原Vue 2实现没有内置取消监听的逻辑,迁移后建议在所有注册事件的组件的
onUnmounted生命周期中,手动调用off方法移除对应回调,避免组件销毁后事件触发导致的内存泄漏。 - 如果原业务代码中用到了一次性事件监听的逻辑,只需要在自定义的类中补充
once方法:注册回调时标记为一次性,触发一次后自动从回调集合中移除即可,不需要改动业务调用逻辑。
内容的提问来源于stack exchange,提问作者user1469734
相关产品推荐
相关产品推荐

