在 vue 3 composition api 中,setup 内必须通过 defineemits 显式声明 emit 函数才能触发自定义事件,需在顶层同步调用,支持数组或类型安全的对象形式声明,并配合 defineprops 构成父子通信契约。

在 Vue 3 的 Composition API 中,setup 函数内不能直接使用 this.$emit,必须通过 defineEmits 显式声明并获取事件发射函数,才能安全触发自定义事件。
✅ 正确声明和使用 defineEmits
defineEmits 是一个编译时宏(仅在 <script setup></script> 中可用),需在 setup 顶层调用,返回一个可调用的 emit 函数。
- 声明方式:直接写
const emit = defineEmits(['update:modelValue', 'submit', 'error'])—— 列出允许触发的事件名数组 - 类型安全(推荐):配合 TypeScript 使用对象形式,明确每个事件的参数类型:
const emit = defineEmits({<br> 'update:modelValue': (value: string) => true,<br> submit: (data: { id: number; name: string }) => true<br>}) - 调用方式:直接
emit('submit', { id: 1, name: 'Alice' }),参数从第二个开始传入,对应事件定义中的形参
⚠️ 常见错误与注意事项
不按规范使用会导致运行时无响应或类型报错:
- 不能在条件语句或函数内部调用
defineEmits—— 必须在setup顶层同步执行 - 未在数组/对象中声明的事件名,调用时不会报错但会被忽略(开发模式下控制台有警告)
- 如果组件同时用了
v-model,建议显式声明'update:modelValue'(或自定义修饰符如'update:count'),否则子组件无法触发更新 -
defineEmits不会自动继承父作用域的emit,它只提供当前组件实例的事件发射能力
? 实际示例:输入框封装触发更新
比如封装一个支持 v-model 的输入组件:
<script setup><br>const props = defineProps({ modelValue: String })<br>const emit = defineEmits(['update:modelValue', 'change'])<br><br>const handleChange = (e: Event) => {<br> const value = (e.target as HTMLInputElement).value<br> emit('update:modelValue', value)<br> emit('change', value)<br>}<br></script>
? 补充:与 defineProps 的配合逻辑
defineEmits 和 defineProps 都是顶层宏,没有执行顺序依赖。但实际开发中通常先声明 props(用于读取),再声明 emit(用于响应)。它们共同构成父子组件通信契约:父传数据(props),子发通知(emit)。










