defineemits是组件事件契约的明确声明,用于避免隐式透传、支持ts类型推导、提升devtools调试体验;推荐对象写法校验参数,注意命名与模板监听一致,漏声明将导致类型丢失和重复触发等问题。

在 <script setup></script> 中,defineEmits 不是可选的“语法糖装饰”,而是组件事件契约的明确声明——它让子组件向父组件“说什么、怎么说”变得可推导、可校验、可维护。
为什么要用 defineEmits 而不是直接 $emit?
Vue 3 移除了 .native 修饰符,所有未在 emits 中声明的监听器都会自动落到组件根元素上。如果子组件没声明 click,而父组件写了 @click="handle",就会触发两次:一次是原生点击,一次是 $emit('click') —— 这是常见 bug 源头。
- 显式声明事件名,避免隐式透传导致的重复触发
- 支持 TypeScript 类型推导,IDE 能提示合法事件和参数结构
- 配合 Vue Devtools,事件流更清晰,调试时能直接看到哪些事件被允许触发
两种主流写法及适用场景
数组写法(适合快速原型):
仅列出事件名,无校验,适合内部小组件或临时开发。
const emit = defineEmits(['update:modelValue', 'change', 'error'])
对象写法(推荐生产环境):
每个事件可配校验函数,尤其适合接收 payload 的事件。
const emit = defineEmits({
change: (value) => typeof value === 'string' || typeof value === 'number',
submit: (data) => {
return data && typeof data === 'object' && 'id' in data
},
error: null // 不校验,等价于省略
})
与 defineProps 配合的关键细节
事件命名需与父组件监听一致,且注意大小写和短横线转换规则:
- 模板中写
@item-click,事件名应声明为'item-click',不能写成itemClick - 若用驼峰式声明
itemClick,模板中必须用@item-click(Vue 自动转换),但不建议混用 - 涉及 v-model 的事件(如
update:modelValue)必须完整写出,不能简写为update
常见误区与避坑提示
以下写法看似简洁,实则埋雷:
- 漏写
defineEmits却在代码里调用emit('xxx')→ 开发时无报错,但类型丢失、Devtools 不识别 - 用
emits: ['click']声明后,在子组件内emit('click', $event)→ 父组件@click仍会触发两次(除非同时移除根元素原生绑定) - 数组写法中传入带参数的事件名,如
['submit(payload)']→ 无效,括号只是注释,不参与校验
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










