你的浏览器无法正常显示内容,请更换或升级浏览器!

Vue3 组件通信的七种方式与选型

tenfei
tenfei
发布于2026-10-16 16:34 阅读15次
Vue3 组件通信的七种方式与选型
同一个需求换了三套通信方案,每次都要动一片代码。本文把 Vue 3 常用的七种通信方式按使用成本从低到高排列,逐个说明适用场景与易错点,最后给出一条六步选型决策路径,帮助在 props、provide、Pinia 之间做出不返工的选择。
## 问题 一个 Vue 3 后台项目,父子孙三层组件,需要把一个表单的校验状态同步给最外层。第一版用了 `provide/inject` 硬传,第二版改成事件总线,第三版上了 Pinia——每换一次方案都要动一片代码。 组件通信这件事,方法不少,但选错了维护成本很高。这篇把常用方式和适用场景捋清楚。 ## 七种方式速查 | 方式 | 方向 | 适用场景 | |---|---|---| | props / emit | 父 → 子 | 最常用,首选 | | v-model | 父 ↔ 子 | 表单类组件封装 | | ref 模板引用 | 父 → 子 | 命令式调用子组件方法 | | provide / inject | 祖先 → 后代 | 跨层配置、主题、国际化 | | 事件总线 | 任意 | 慎用,难追踪 | | Pinia / Vuex | 任意 | 跨路由共享状态 | | 路由参数 / query | 页面间 | 跨页面传递 | ## 1. props / emit 永远的第一选择。约定清晰,数据流单向,DevTools 里能直接看到组件树上的 props。 ```vue <script setup> const props = defineProps<{ modelValue: string }>(); const emit = defineEmits<{ 'update:modelValue': [v: string] }>(); </script> ``` 原则:**能用 props 解决的,不要想别的。** ## 2. v-model `v-model` 本质是 `modelValue` + `update:modelValue` 的语法糖。封装输入框、开关、选择器这类组件时用它,父组件写法最自然。 一个组件可以绑定多个 v-model: ```vue <MyForm v-model="name" v-model:page="pageNo" /> ``` 对应子组件里 `modelValue` 和 `page` 两个 props。 ## 3. 模板 ref 需要命令式调用子组件方法时用,比如"点击父组件按钮,让子组件的表格刷新": ```ts const tableRef = ref<InstanceType<typeof MyTable>>(); tableRef.value?.refresh(); ``` 注意子组件要用 `defineExpose` 显式暴露,否则默认只能拿到 `$el`。 ## 4. provide / inject 跨层传递,避免一层层 props 钻探。适合主题、语言、全局配置这类"注入型"数据。 ```ts // 祖先 provide('tableConfig', { size: 'small' }); // 后代 const cfg = inject<{ size: string }>('tableConfig'); ``` 坑:**inject 的值是普通对象时不会自动响应式**。要传响应式数据就用 `ref` 或 `computed` 包一层,或者用 `provide` 传 `readonly` 的 reactive。 另外 inject 没有默认值保护时要写第二个参数,否则找不到就是 undefined,报错很晚才发现。 ## 5. 事件总线 用 `mitt` 之类的小库实现跨组件通信。看起来方便,实际是灾难——发出去了不知道谁在收,出问题无法追踪。 只在两种情况下用:组件关系极远、且通信是一次性的(比如"全局通知弹窗关闭")。**常规业务别碰。** ## 6. Pinia 跨路由共享状态的标准答案。用户信息、权限、字典数据、购物车这类放这里。 ```ts const user = useUserStore(); user.name = 'x'; // 直接改 ``` Pinia 没有 mutations,action 里写异步逻辑。比 Vuex 简洁很多,Vue 3 项目直接选它,不用犹豫。 ## 7. 路由参数 跨页面传递首选路由参数或 query,刷新后还在。需要"可分享的链接"就必须走这条路,而不是存 Pinia。 ## 怎么选 按这个顺序问自己: 1. 父子之间?→ props / emit 2. 表单封装?→ v-model 3. 要调子组件方法?→ 模板 ref + defineExpose 4. 跨层但同分支?→ provide / inject 5. 跨路由?→ Pinia 6. 跨页面且要可分享?→ 路由参数 **越往下越重,成本越高。** 能在第 1 层解决的,不要用到第 6 层。 ## 最后 通信方式的选择,本质上是在选"数据流有多可预测"。props 单向可追,事件总线完全不可追。工程上宁可多写几行 props,也别图省事上全局总线——半年后接手代码的人会感谢你。

1

0

文章点评
赞助商广告位
Copyright © from 2021 by namoer.com
458815@qq.com QQ:458815
蜀ICP备2022020274号-2