vue子组件通过$emit向父组件通信是最自然解耦的方式,需规范事件名(kebab-case)、参数(扁平化)、触发时机(用户操作后),并配合defineemits声明、v-model语法糖及避免常见坑点。

子组件通过 $emit 主动“喊话”父组件,是 Vue 中最自然、最解耦的子传父方式。关键不在怎么写,而在事件名是否统一、参数是否轻量、时机是否合理。
子组件怎么 emit 才算规范
必须先明确可触发哪些事件(尤其 Vue 3 的 defineEmits 是推荐写法),再调用 emit 发送:
- Vue 3 Composition API 中,用
const emit = defineEmits(['update-user', 'submit-form'])显式声明,后续只能 emit 这些名称 - 事件名统一用 kebab-case(如
user-selected),不写userSelected或userslected,避免大小写和拼写歧义 - 参数尽量扁平:传
{ id: 1, name: '张三' }比传整个用户对象更安全;多个值可拆成emit('user-change', id, name),但形参顺序要和父组件严格对应 - 别在
mounted或计算属性里乱 emit,应在用户操作后(如按钮点击、输入失焦、API 响应完成)才触发
父组件监听时的常见写法与细节
监听不是简单加个 @ 就完事,接收逻辑直接影响可维护性:
- 模板中直接写内联表达式仅限极简场景,例如:
@user-change="userId = $event" - 多数情况应在
methods或setup里定义处理函数,比如:@user-change="handleUserChange",然后在函数里解构或赋值 - 若子组件 emit 的是对象,父组件可直接解构接收:
handleUserChange({ id, name }) { this.currentId = id; this.currentName = name; } - 注意事件名大小写:子组件 emit
'data-updated',父组件必须写@data-updated,写成@dataUpdated就监听不到
v-model 是 emit 的语法糖,不是替代方案
它本质还是基于 update:xxx 类型的 emit,只是让绑定更简洁:
- 子组件内部不直接改
props.modelValue,而是emit('update:modelValue', newValue) - 父组件写
<child v-model="inputText"></child>,等价于<child :model-value="inputText"> inputText = val" /></child> - 自定义 v-model 的 prop 名(如
v-model:title)时,子组件需 emit'update:title',不能漏掉update:前缀
容易踩坑的几个点
看似简单,但实际项目中高频出错:
- 子组件忘了在
emits选项中声明事件(Vue 3 警告但不报错;Vue 2 则完全无提示) - 父组件监听了事件,但没给子组件绑定任何 props,导致子组件无法初始化或行为异常
- 在 emit 后立刻读取父组件同步更新后的数据,却忽略了响应式更新是异步的,应使用
nextTick或 watch 监听 - 把原生事件名(如
click、input)当自定义事件名使用,造成透传干扰或行为不可预期
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










