Vue组件通信遵循“数据向下、事件向上”的单向流协议:props是父组件向子组件传递配置与状态的交付清单,必须声明类型、默认值并禁止子组件修改;emits是子组件向父组件发送标准化事件的上报通道,需提前声明、语义化命名,承载结构化载荷。

Vue 组件通信不是随意传值或乱发事件,而是一套有明确边界、方向和责任的协作协议。核心就一条:数据向下流动,事件向上通知,组件各守其职。
Props:父组件的“交付清单”
父组件通过 Props 向子组件传递配置、状态或业务数据,相当于一份带说明的交付物。它不是共享内存,而是单次快照(响应式引用除外)。
- 必须用 对象写法声明类型、是否必填、默认值,避免运行时类型错误
- 数组/对象类默认值必须用函数返回,例如
default: () => [] - 子组件内禁止直接修改 props,哪怕只是临时改个字段——该改数据,由父组件改;该触发行为,用事件通知
- 模板中可直接使用
props.xxx或解构后用xxx(<script setup> 中自动解包)</script>
Emits:子组件的“上报通道”
子组件不主动改变父状态,而是通过 emit 发出标准化事件,告诉父组件“发生了什么”“需要怎么处理”。这本质是契约式回调。
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 必须提前在
defineEmits中声明事件名,否则 TypeScript 无法推导,IDE 也无提示 - 推荐使用语义化命名,如
update:visible、submit:form,便于理解意图 - v-model 的语法糖就是基于 emit 实现的:
v-model:show="flag"等价于:show="flag" @update:show="v => flag = v" - 事件载荷建议结构化,比如传对象
{ id, value, timestamp },而非多个散参数
命名与类型:跨团队协作的底线
通信协议一旦写进组件 API,就具备了接口契约属性。命名和类型不是可选项,而是维护性前提。
立即学习“前端免费学习笔记(深入)”;
- Prop 名统一用 camelCase(JS 侧),HTML 模板中可用 kebab-case(如
user-name对应userName) - 结合 TypeScript 使用
PropType,尤其是对象、联合类型、自定义接口,例如:user: { type: Object as PropType<userprofile>, required: true }</userprofile> - 对关键字段加
validator,比如校验权限码格式、ID 是否合法,把错误拦截在组件初始化阶段 - 事件名避免模糊词如
change、click,优先用动宾结构:select:item、remove:tag
边界意识:什么不该做
违反通信协议看似省事,实则埋下耦合、调试困难、不可测试的隐患。
- 不用
$refs直接调子组件方法传参——这破坏封装,让子组件无法独立复用 - 不在子组件里修改父级 prop 引用的对象属性(如
props.user.name = 'new')——虽能响应,但违背单向流原则,父组件失去控制权 - 不为图方便在深层嵌套中层层透传 props(所谓“prop drilling”)——该用 provide/inject 或提升状态时就果断重构
- 不把业务逻辑塞进事件名里,比如
@do-something-and-then-save——事件只描述发生了什么,后续动作由父组件决定

















