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
相关产品推荐
相关产品推荐

