Vue官方推荐子组件通过defineEmits声明并触发自定义事件(如emit('confirmDelete', data)),父组件用@监听(如@confirmDelete="handleDelete")接收参数,严格遵循单向数据流原则,语义清晰、类型安全、调试友好。

子组件不能直接调用父组件的方法,必须通过自定义事件“喊话”——也就是用 emit 触发一个事件,让父组件监听并响应。这是 Vue 官方推荐、最稳定也最符合数据流原则的方式。
子组件:先声明,再触发
在 <script setup></script> 中,用 defineEmits 明确告诉别人“我会发哪些事件”,然后调用它传数据:
- 声明事件名(推荐显式写,利于类型检查和维护):
const emit = defineEmits(['confirmDelete', 'updateUser']) - 触发时直接调用:
emit('confirmDelete', { id: 101, name: '张三' }) - 不传参也能触发:
emit('updateUser')
父组件:监听 + 接收参数
在模板里用 @事件名 绑定处理函数,函数参数就是子组件传来的值:
- 写法示例:
- 对应方法中接收参数:
const handleDelete = (data) => { console.log(data.id) } - 注意别写成
@confirmDelete="$emit('xxx')"——那是错的,$emit是子组件内部方法,父组件模板里不能直接调用
常见踩坑点
很多新手卡在这几步,其实问题很集中:
- 子组件没写
defineEmits声明,或拼错了事件名(大小写、连字符要完全一致) - 父组件监听时用了错误的语法,比如
v-on="$emit()"或漏掉@符号 - 把父组件方法当 props 传进子组件再调用——这违反单向数据流,会报
is not a function - 事件名和原生 DOM 事件重名(如
click),Vue 会优先处理自定义事件,原生事件就失效了
对比其他方式:为什么推荐 emit?
有人试过把父组件方法当 props 传下去,或者用插槽加作用域插槽来“反向调用”,但这些都不如 emit 清晰可靠:
-
props + 函数:破坏单向数据流,调试困难,Vue 官方明确不鼓励 -
插槽传函数:适合内容渲染逻辑解耦,不是为“子调父”设计,容易绕晕且不易追踪 -
emit:语义明确(子通知父)、结构清晰(谁发谁收)、支持 TS 类型推导、Vue Devtools 可见可调试









