vue3事件总线与emit

1.vue3为什么去掉了onoff?

1.设计理念的调整

Vue 3 更加注重组件间通信的明确性和可维护性。$on 这类事件 API 本质上是一种 "发布 - 订阅" 模式,容易导致组件间关系模糊(多个组件可能监听同一个事件,难以追踪事件来源和流向)。Vue 3 推荐使用更明确的通信方式,如: - 父子组件通过 propsemit 通信 - 跨组件通信使用 provide/inject 或 Pinia/Vuex 等状态管理库 - 复杂场景可使用专门的事件总线库(如 mitt

2.与 Composition API 的适配

Vue 3 主推的 Composition API 强调逻辑的封装和复用,而 $on 基于选项式 API 的实例方法,与 Composition API 的函数式思维不太契合。移除后,开发者可以更自然地使用响应式变量或第三方事件库来实现类似功能。

3.减少潜在问题

  • $on 容易导致内存泄漏(忘记解绑事件)
  • 事件名称可能冲突(全局事件总线尤其明显)
  • 不利于 TypeScript 类型推断,难以实现类型安全

vue3中如何使用事件总线实现跨级组件之间的通信?

1.可以通过第三方库(如 mitttiny-emitter)替代,示例如下:

js 复制代码
// 安装 mitt:npm install mitt
import mitt from 'mitt'

// 创建事件总线实例
const emitter = mitt()

// 监听事件
emitter.on('event-name', (data) => {
  console.log('收到事件:', data)
})

// 触发事件
emitter.emit('event-name', { message: 'hello' })

// 移除事件监听
emitter.off('event-name', handler)

2.使用 Vue3 提供的 provide/inject

js 复制代码
// 父组件提供事件总线
import { provide, ref } from 'vue'

export default {
  setup() {
    const events = ref({})
    
    const on = (name, callback) => {
      events.value[name] = callback
    }
    
    const emit = (name, data) => {
      if (events.value[name]) {
        events.value[name](data)
      }
    }
    
    provide('eventBus', { on, emit })
  }
}

// 子组件使用
import { inject } from 'vue'

export default {
  setup() {
    const eventBus = inject('eventBus')
    
    // 监听事件
    eventBus.on('event-name', (data) => {
      // 处理逻辑
    })
    
    // 发送事件
    eventBus.emit('event-name', data)
  }
}

3.利用 Vue 实例的自定义事件 虽然 Vue3 移除了 $on$off 等方法,但可以创建一个空的 Vue 实例作为事件总线,利用其自定义事件 API:

js 复制代码
// eventBus.js
import { createApp } from 'vue'
const app = createApp({})
export default app



// 发送事件
import bus from './eventBus'
bus.config.globalProperties.$emit('event-name', data)

// 监听事件(在组件中)
import { getCurrentInstance } from 'vue'
export default {
  mounted() {
    const instance = getCurrentInstance()
    instance.appContext.config.globalProperties.$on('event-name', (data) => {
      // 处理逻辑
    })
  }
}

4.使用 reactive 创建事件总线

js 复制代码
// 组件A中发送事件
import eventBus from './eventBus'
eventBus.emit('user-updated', { name: '张三' })

// 组件B中监听事件
import eventBus from './eventBus'
export default {
  mounted() {
    this.handleUserUpdate = (user) => {
      console.log('用户更新了', user)
    }
    eventBus.on('user-updated', this.handleUserUpdate)
  },
  beforeUnmount() {
    // 组件卸载时移除监听,避免内存泄漏
  1. 使用 Pinia/Vuex 状态管理

对于复杂应用,更推荐使用状态管理库来处理组件间通信,通过修改共享状态来实现组件间的数据传递。

总结

  • 在 Vue3 中实现事件总线,最推荐的方式是使用 mitt 库,它轻量高效且 API 简洁,能够很好地替代 Vue2 中的事件总线功能。对于简单场景也可以使用 provide/inject 方案,但对于大型应用,状态管理库会是更优选.择。
  • 手动实现的事件总线需要注意在组件卸载时移除事件监听,避免内存泄漏; 注意考虑 "同一事件绑定多个回调" 的去重逻辑;避免没有事件触发时的异常捕获,单个回调报错可能阻断整个事件流程。

2.vue3中的defineEmits $emit又是什么关系?

Vue3 并没有完全移除 $emit(它仍然用于子组件向父组件传递事件)。

defineEmits是 Vue3 提供的编译时宏命令 (Compiler Macro),用于在 <script setup> 语法中声明组件可以触发的事件,主要作用是:

  1. 明确组件对外暴露的事件,提升代码可读性和可维护性
  2. 提供 TypeScript 类型校验(如果使用 TS)
  3. 在开发环境下对未声明的事件触发给出警告

使用方式(在 <script setup> 中)

vue 复制代码
<template>
  <button @click="handleClick">点击触发事件</button>
</template>

<script setup>
// 声明组件可以触发的事件
const emit = defineEmits(['change', 'update'])

const handleClick = () => {
  // 触发事件并传递数据
  emit('change', 'hello')
  emit('update', { id: 1, name: 'test' })
}
</script>

带类型校验的用法(TypeScript)

vue 复制代码
<script setup lang="ts">
// 用类型标注事件名称和参数类型
const emit = defineEmits<{
  (e: 'change', value: string): void
  (e: 'update', data: { id: number; name: string }): void
}>()

// 错误示例:参数类型不匹配会报错
emit('change', 123) // TS 类型错误
</script>

注意点

  1. 仅在 <script setup> 中可用defineEmits 是编译时宏,不需要导入,直接使用(Vue 编译器会处理)

  2. 替代 Vue2 的 emits 选项 :在非 <script setup> 语法中,仍可以用 emits 选项声明事件:

    javascript 复制代码
    export default {
      emits: ['change', 'update'], // 等价于 defineEmits
      setup(props, { emit }) {
        // ...
      }
    }
  3. $emit 的关系defineEmits 返回的 emit 函数与 this.$emit 功能一致,但在 <script setup> 中推荐使用前者(更符合组合式 API 风格)

相关推荐
丨Sky丨夜吻4 小时前
vscode扩展
ide·vue.js·vscode
岁月向前5 小时前
不同的协议和场景防丢包方案
前端
琢磨先生TT5 小时前
一个前端工程师的年度作品:从零开发媲美商业级应用的后台管理系统!
前端·前端框架
云枫晖5 小时前
JS核心知识-Ajax
前端·javascript
玄魂5 小时前
VTable Gantt 智能 zoom(缩放)功能介绍与开发实践
前端·开源·数据可视化
青灬河5 小时前
实现企业级全栈应用服务框架-Elpis(一)
vue.js·node.js
Joyee6915 小时前
RN 的初版架构——UI 布局与绘制
前端·react native
会点法律的程序员5 小时前
小程序 地理位置授权怎么搞
前端·小程序·uni-app
牛头马面5 小时前
手把手教你在 Taro 小程序中用 axios 替代 Taro.request:@tarojs/plugin-http 配置与拦截器封装
前端