You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移Vue2项目至Vue3时this.$root.$on的替代方案是什么?

问题原因

Vue3 已经彻底移除了实例上自带的$on、$off、$once三个事件API,不再支持直接通过组件实例(包括$root根实例)绑定自定义事件做跨组件通信,所以TS会直接抛出Property '$on' does not exist on type 'ComponentPublicInstance'的类型错误。

替代实现方案

你原有逻辑是在根实例上绑定openLotsenFinder全局事件,触发时移除window上的keyup监听,选下面任意一种方案替换即可,优先选第一种,改造成本最低。

方案1:独立事件总线(适配原有逻辑,改造成本最低)

不需要引入额外第三方依赖,先单独实现一个极简的事件总线工具:

// 存为 src/utils/eventBus.js
const createEventBus = () => {
  const eventMap = new Map()
  return {
    on(event, cb) {
      if (!eventMap.has(event)) eventMap.set(event, [])
      eventMap.get(event).push(cb)
    },
    off(event, cb) {
      if (!eventMap.has(event)) return
      if (!cb) return eventMap.delete(event)
      const cbList = eventMap.get(event)
      const idx = cbList.indexOf(cb)
      if (idx > -1) cbList.splice(idx, 1)
    },
    emit(event, ...payload) {
      if (!eventMap.has(event)) return
      eventMap.get(event).forEach(cb => cb(...payload))
    }
  }
}
export default createEventBus()

然后把原来的$root.$on逻辑替换为引用这个事件总线即可,注意一定要在组件卸载时主动解绑事件,避免内存泄漏:

import eventBus from '@/utils/eventBus'

export default {
  data() {
    return {
      eventsOnLotsenFinder: false
    }
  },
  mounted() {
    if (!this.eventsOnLotsenFinder) {
      // 替换原有 this.$root.$on 逻辑
      eventBus.on('openLotsenFinder', this.handleLotsenFinderOpen)
      this.eventsOnLotsenFinder = true
    }
  },
  // Vue3 卸载生命周期钩子改名了,对应Vue2的beforeDestroy
  beforeUnmount() {
    eventBus.off('openLotsenFinder', this.handleLotsenFinderOpen)
  },
  methods: {
    handleLotsenFinderOpen() {
      window.removeEventListener('keyup', this.onWindowKeyUp)
    },
    onWindowKeyUp() {
      // 原有keyup监听逻辑保持不变
    }
  }
}

项目里所有原来触发this.$root.$emit('openLotsenFinder')的位置,统一替换为eventBus.emit('openLotsenFinder')就能跑通原有逻辑。

小提示:原来的写法直接把匿名箭头函数传进$on,后续没法精准定位回调做解绑,很容易造成内存泄漏,改造的时候最好把回调抽成组件方法,方便绑定和解绑。

方案2:全局状态驱动(适合关联全局业务的场景)

如果openLotsenFinder的触发和全局业务状态强关联,可以直接用Pinia管理对应状态,在当前组件watch这个状态的变化,状态变更时执行移除keyup监听的逻辑即可,不需要走事件通信。

方案3:provide/inject 跨层级通信

如果触发事件的组件和当前组件是固定的祖先/后代关系,可以在根组件通过provide暴露触发回调,后代组件通过inject拿到回调后触发,这种方式适合层级固定的场景,灵活度不如独立事件总线。

注意:不要尝试手动给Vue3组件实例挂载$on方法做兼容,后续版本迭代会有隐性bug,TS类型支持也很差。

内容的提问来源于stack exchange,提问作者Pazulay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 15:18:15