Vue3 组件通信方式怎么选:一份实用决策指南
Vue3 里父传子、子传父、兄弟组件、跨层级通信,可选方案一大堆,到底该用哪个?这篇不讲概念堆砌,而是按真实场景给出决策路径:什么情况用 props 和 emit 就够,什么时候需要 provide 和 inject,什么场景才该上状态管理库,并附上几个容易误用的反例,帮你做出合适的选择。
# 一、先问自己三个问题
选通信方案前,先明确:
1. **数据流向**是单向还是双向?
2. **层级关系**是父子、兄弟还是跨多層?
3. **这份数据**是局部状态还是全局共享?
答案清楚了,方案基本就确定了。
# 二、四种场景对应方案
## 场景一:父子通信 —— props 加 emit
最常见也最推荐的方式,**保持单向数据流**:
```vue
<!-- 父组件 -->
<Child :title="title" @update="handleUpdate" />
```
```js
// 子组件
const props = defineProps({ title: String })
const emit = defineEmits(["update"])
emit("update", newValue)
```
子组件**永远不要直接改 props**,要改就让父组件改。这条守住了,数据流就清晰。
## 场景二:兄弟组件 —— 状态提升到父级
两个平级组件要共享数据,最简单的办法是把状态**提到共同父组件**,通过 props 下发、emit 上报。能用这个方案就别急着引入复杂工具。
## 场景三:跨多層传递 —— provide 和 inject
祖孙组件之间隔了好几层时,一层层传 props 太痛苦,用依赖注入:
```js
// 祖先组件
provide("theme", themeConfig)
// 后代组件(任意深度)
const theme = inject("theme")
```
注意:provide 传的值**默认不是响应式的**,要响应式就传 ref 或 reactive 对象。
## 场景四:全局共享 —— 状态管理库
用户信息、购物车、权限这类**跨页面共享**的数据,才需要考虑 Pinia 这类状态管理。
# 三、几个常见反例
1. **什么都往状态管理里塞**——局部表单状态也放全局,维护成本飙升;
2. **子组件直接改 props**——数据流向混乱,出 bug 极难排查;
3. **用事件总线满天飞**——小项目还行,项目一大就变成"谁发的这个事件"之谜;
4. **滥用 provide**——它适合主题、配置这类静态或低频数据,高频变动的用状态管理更合适。
# 四、决策速查表
| 场景 | 推荐方案 |
| --- | --- |
| 父到子 | props |
| 子到父 | emit |
| 兄弟组件 | 状态提升到父级 |
| 隔代传递 | provide / inject |
| 全局共享 | 状态管理库 |
| 表单双向 | v-model |
# 五、小结
选择的原则是**用最简单的方案解决问题**。props 加 emit 能覆盖八成场景,剩下的再用进阶方案。过早引入复杂状态管理,往往是后来项目难以维护的起点。