defineEmits 是 Vue 3 组合式 API 中子组件向父组件通信的核心方式,替代 this.$emit,支持类型安全、运行时校验与 v-model 双向绑定,需顶层声明且避免混用 setup 参数。

在 Vue 3 的组合式 API 中,defineEmits 是子组件向父组件通信的核心方式,它替代了 Options API 中的 this.$emit,更类型安全、更直观。正确使用它,能让父子组件解耦清晰、逻辑可读、TS 支持友好。
defineEmits 基础用法:声明 + 触发
子组件中需先用 defineEmits 声明允许抛出的事件,再通过返回的函数触发:
- 不带参数时,仅做类型提示(运行时无校验):
const emit = defineEmits(['update:modelValue', 'submit']) - 带对象形式可启用运行时校验和 TS 类型推导:
const emit = defineEmits({ 'click': (e: MouseEvent) => true, 'change': (val: string) => true }) - 触发事件直接调用:
emit('submit', { id: 1, name: 'Vue' })—— 参数会依次传给父组件的监听函数
与 v-model 配合:实现双向绑定语义
当子组件封装表单控件(如自定义输入框),可通过 defineEmits 支持 v-model,关键是声明 update:xxx 事件:
- 默认 v-model 绑定
modelValue,子组件需声明:const emit = defineEmits(['update:modelValue']) - 在 input 或 change 时触发:
<input @input="e => emit('update:modelValue', e.target.value)"> - 父组件写法保持简洁:
<MyInput v-model="searchText" />,等价于v-bind:model-value="searchText" v-on:update:model-value="val => searchText = val"
TS 类型精准支持:避免 any 和运行时错误
利用泛型 + 联合类型,让 emit 的参数在编辑器中自动提示、编译期报错:
立即学习“前端免费学习笔记(深入)”;
- 推荐写法(TS 友好):
const emit = defineEmits<{ (e: 'add', item: { id: number; name: string }): void; (e: 'remove', id: number): void }>() - 这样父组件监听时:
@add="addItem" → addItem(item: { id: number; name: string }),参数类型自动推导 - 若传参错误(如
emit('add', 'wrong')),TS 编译直接报错,而不是运行时静默失败
常见误区与避坑提醒
实际开发中容易忽略的细节:
- defineEmits 必须顶层调用:不能放在 if、function 或异步回调里,否则编译报错
-
不要混用 defineEmits 和 setup() 返回的 emit:组合式 API 中不再有 setup 的第二个参数
emit,全靠defineEmits声明 -
事件名尽量用 kebab-case:虽然驼峰也支持,但 HTML 模板中监听必须用短横线(如
@item-click),统一风格更稳妥 -
避免过度 emit:一个操作只 emit 一个语义明确的事件,比如
submit-success比update+notify+scroll-top更易维护


















