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

Vue3如何通过JavaScript代码添加事件监听器?

Vue3中通过JavaScript代码监听子组件自定义事件的方法

Vue3里没法用this.$refs.someChild.$on()这种方式监听子组件的自定义事件——因为Vue3已经移除了组件实例上的$on、$off这类事件总线API。同时addEventListener也不行,它是用来监听DOM原生事件的,和Vue组件的自定义事件体系不兼容。

下面提供两种可行的实现方式:

方案一:子组件暴露注册回调的方法(推荐)

让子组件提供一个手动注册事件回调的方法,父组件通过$refs调用该方法添加监听,子组件触发事件时同时执行手动注册的回调和原生emit的事件。

子组件代码

<template>
  <!-- 子组件内容,比如一个按钮触发事件 -->
  <button @click="triggerEvent('hello from child')">触发事件</button>
</template>

<script setup>
import { ref, defineEmits } from 'vue'

// 定义要触发的事件
const emit = defineEmits(['someEvent'])

// 存储手动注册的回调函数
const someEventCallbacks = ref([])

// 暴露给父组件的注册方法
const onSomeEvent = (callback) => {
  someEventCallbacks.value.push(callback)
}

// 触发事件的逻辑
const triggerEvent = (msg) => {
  // 触发原生emit的事件(供模板v-on监听)
  emit('someEvent', msg)
  // 执行所有手动注册的回调
  someEventCallbacks.value.forEach(cb => cb(msg))
}

// 暴露方法给父组件
defineExpose({
  onSomeEvent
})
</script>

父组件代码

<template>
  <SomeChild ref="someChild" @some-event="handleTemplateEvent" />
</template>

<script setup>
import { ref, onMounted } from 'vue'
import SomeChild from './SomeChild.vue'

const someChild = ref(null)

onMounted(() => {
  // 通过$refs调用子组件的方法添加监听
  someChild.value?.onSomeEvent((msg) => {
    console.log('手动注册的事件触发:', msg)
  })
})

const handleTemplateEvent = (msg) => {
  console.log('模板v-on的事件触发:', msg)
}
</script>

方案二:自定义事件总线(不推荐)

如果需要跨组件(非父子)监听,或者习惯事件总线的方式,可以自己封装一个简单的事件发射器,但要注意及时销毁监听避免内存泄漏。

事件总线封装

// eventBus.js
class EventBus {
  constructor() {
    this.events = {}
  }

  // 注册监听
  on(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = []
    }
    this.events[eventName].push(callback)
  }

  // 触发事件
  emit(eventName, ...args) {
    this.events[eventName]?.forEach(callback => callback(...args))
  }

  // 移除监听
  off(eventName, callback) {
    if (!this.events[eventName]) return
    this.events[eventName] = this.events[eventName].filter(cb => cb !== callback)
  }
}

export default new EventBus()

子组件使用

<script setup>
import { defineEmits } from 'vue'
import eventBus from './eventBus.js'

const emit = defineEmits(['someEvent'])

const triggerEvent = (msg) => {
  emit('someEvent', msg)
  // 同时通过总线触发事件
  eventBus.emit('someEvent', msg)
}
</script>

父组件使用

<script setup>
import { onMounted, onUnmounted } from 'vue'
import eventBus from './eventBus.js'

const handleBusEvent = (msg) => {
  console.log('总线监听的事件触发:', msg)
}

onMounted(() => {
  eventBus.on('someEvent', handleBusEvent)
})

onUnmounted(() => {
  // 组件销毁时移除监听,防止内存泄漏
  eventBus.off('someEvent', handleBusEvent)
})
</script>

注意事项

  • 方案一更符合Vue的组件通信规范,父子组件交互清晰,便于维护和调试
  • 方案二的事件总线容易导致组件间耦合度升高,Vue3官方更推荐使用Pinia或props/emit的方式进行状态和事件传递

内容的提问来源于stack exchange,提问作者Arno van Oordt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 04:50:10