组件通信应分层设计:父子用props/emit,深层透传用provide/inject(推荐Symbol键,保留ref引用),业务状态共享才用Pinia模块化store;避免事件总线和$parent/$children滥用,兄弟通信由父级统一管理;可封装provide/inject为可复用Hook。

处理组件间复杂依赖传递,关键不是堆砌工具,而是按需分层:简单父子用 props + emit,跨多层用 provide/inject,真正共享且需响应式追踪的业务状态,才交给 Pinia 管理。
优先用 provide/inject 解决“深层透传”问题
当某个状态只在某块功能区域(比如一个表单容器及其所有子表单项)内流动,又不想污染全局,provide/inject 是最轻量、最语义清晰的选择。
- 祖先组件调用
provide,传入响应式对象(如ref或reactive),后代组件用inject获取,天然保持响应性 - 注入名建议用
Symbol,避免字符串命名冲突;若用字符串,确保在整个组件树中唯一 - 不要在
inject时直接解包 ref 的值(如inject('count').value),应保留 ref 引用,才能持续响应上游变更
Pinia 不是全局仓库,而是模块化状态单元
别把所有状态都塞进一个 useAppStore()。Pinia 的优势在于模块自治——每个业务模块定义自己的 store,再由父组件按需提供给子树。
- 在父组件 setup 中创建 store 实例(
const formStore = useFormStore()),然后通过provide注入,子组件只 inject 自己需要的那个 store - 这样既复用了 Pinia 的 devtools 调试、持久化、时间旅行等能力,又把状态作用域限制在局部组件树,避免全局污染
- store 文件本身不导出实例,只导出
defineStore工厂函数,保证 tree-shaking 和测试隔离
避免误用事件总线或 $parent/$children
mitt 或自定义事件总线适合松耦合、一次性通知(如“用户登出”),但不适合承载核心业务状态流;$parent 和 $children 则破坏封装、不可靠、非响应式,应视为最后手段。
立即学习“前端免费学习笔记(深入)”;
- 如果发现自己频繁用事件总线同步两个组件的状态,说明这部分数据本该抽成共享 store 或由共同父级 provide
- 兄弟组件通信,优先考虑让它们的共同父组件 hold 状态,并通过 props 或 provide 分发,而非绕过父级直连
- 任何需要“监听变化→触发更新”的场景,都应优先走响应式路径(ref / reactive / store),而不是手动 emit/listen
组合式逻辑可封装为可复用的 provide/inject Hook
把常见依赖逻辑(如表单校验上下文、分页控制、权限判断)抽成独立 hook,内部完成 provide 和 inject 的配对,对外只暴露简洁 API。
- 例如
useFormContext()在父组件调用时自动 provide 表单状态和方法,在子组件调用时自动 inject 并返回校验函数和错误信息 - 这样业务组件无需关心底层是用 Pinia 还是 reactive,也不用写重复的 provide/inject 代码
- hook 内部可灵活切换实现:开发期用本地 reactive,上线后换为注入的 Pinia store,上层组件无感知


















