核心是组件职责单一、接口明确:只通过声明式 props 接收只读配置,用语义化 emit 事件(如 'update:model-value')反馈状态,不隐式依赖父级或全局状态,确保高内聚低耦合。

核心在于让每个组件只管好自己的事,数据和行为都通过明确、受控的接口进出。不是“能传就行”,而是“该传什么、怎么传、谁负责改”全说清楚。
明确职责边界:一个组件只做一件事
比如做一个搜索输入框,它不决定搜完去哪、结果怎么展示、要不要加防抖——这些都交给父组件或组合式函数。它只负责:
- 显示输入框和清空按钮
- 响应用户输入,把值发出去
- 接收外部传入的当前值(支持 v-model)
- 不主动调用 API、不修改路由、不读取 localStorage
Props 设计要「只读 + 可配置」
所有输入必须走 props,且声明清晰类型与默认值。避免子组件自己猜逻辑:
- 用 defineProps 显式定义,不靠 this.$props 或隐式推断
- 必填项标 required: true,可选字段给 default(函数需返回新对象)
- 状态类 props(如 loading、disabled)不带副作用,仅用于控制 UI 表现
- 避免传“整个对象”或“一堆回调”,按需拆解:传 label 就别再传 labelClass 和 labelStyle,合并进 label 配置项更内聚
Emit 事件要「语义化 + 可预测」
子组件不直接改父组件数据,只发“发生了什么”。父组件决定“接下来怎么做”:
- 用 defineEmits 声明事件名,推荐 kebab-case(如 'update:model-value')以兼容 v-model
- 事件名表达意图,不是操作:用 'submit' 而非 'clickButton',用 'remove-item' 而非 'deleteFromList'
- 携带最小必要数据:提交表单 emit('submit', { username, password }),不传整个表单实例或 DOM 元素
- 需要双向绑定时,统一用 update:xxx 模式,配合 v-model 自动解构
拒绝隐式依赖,用接口代替猜测
高内聚低耦合不是靠“少写点代码”,而是靠“多写点契约”:
- 组件内部不访问 $parent、$root、this.$refs(除非极特殊场景)
- 不直接修改全局状态(Pinia store 也应通过事件通知后由父层 commit)
- 测试时,能单独 mount 组件、传 mock props、监听 emit,不依赖真实父组件就能验证行为
- 类型系统是帮手:配合 TypeScript,props 和 emits 的定义就是天然的接口文档


















