Vue 中组件通信有哪些方式?该用哪个?

📅 2026/7/30 10:16:38
Vue 中组件通信有哪些方式?该用哪个?
Vue 中组件通信有哪些方式该用哪个一句话总结父子用 props/emit跨级用 provide/inject兄弟用状态提升或 EventBus全局用 Pinia。按关系选方案别上来就 Vuex/Pinia正文目录Vue 组件通信全景图5 大高频场景 最佳实践万能兜底按关系选方案预防 checklist不再踩坑一句话总结一、Vue 组件通信全景图Vue 组件通信的本质就是数据怎么从 A 传到 BB 怎么通知 A。按组件关系分类关系推荐方案一句话父 → 子props父亲给孩子东西子 → 父emit孩子喊一声通知父亲父 → 子直接操作ref expose父亲直接伸手祖先 → 后代provide / inject祖宗传家宝后代直接拿兄弟之间状态提升 / EventBus找共同的父亲中转任意组件Pinia全局仓库谁都能取常见误区上来就 Pinia简单父子通信也套全局状态代码复杂度暴增。二、5 大高频场景 最佳实践① 父子通信props 传了对象子组件直接改!-- ❌ 子组件直接修改 props 对象的属性 -- script setup const props defineProps([user]); const changeName () { props.user.name new; // ❌ 虽然能改但数据流不清晰 }; /script修复子组件 emit父组件改!-- ✅ 子组件 emit 事件父组件修改数据 -- script setup const props defineProps([user]); const emit defineEmits([update]); const changeName () { emit(update, { ...props.user, name: new }); // ✅ emit 新对象 }; /script② 跨级通信层层透传 propsprop drilling!-- ❌ 中间组件不需要这个 prop只是透传 -- !-- Grandfather.vue -- Template :themetheme / !-- Father.vue (不需要 theme但必须接) -- script setup defineProps([theme]); // ❌ 纯透传 /script template Child :themetheme / /template !-- Child.vue -- script setup defineProps([theme]); /script修复用 provide/inject!-- ✅ 祖先组件 provide -- !-- Grandfather.vue -- script setup import { ref, provide } from vue; const theme ref(dark); provide(theme, theme); // ✅ 注入 /script template Father / /template!-- ✅ 后代组件 inject中间层完全不需要知道 -- !-- Child.vue -- script setup import { inject } from vue; const theme inject(theme); // ✅ 直接拿 /script③ 兄弟通信用 EventBus 但忘记销毁// ❌ EventBus 方案组件销毁时没移除监听 → 内存泄漏importmittfrommitt;constbusmitt();// BrotherA.vuebus.emit(refresh,data);// BrotherB.vuebus.on(refresh,handler);// ❌ 没有 bus.off切走后还在监听修复方案一onUnmounted 时移除监听// ✅ 组件销毁时移除import{onUnmounted}fromvue;bus.on(refresh,handler);onUnmounted((){bus.off(refresh,handler);// ✅ 清理});修复方案二推荐状态提升到共同父组件!-- ✅ 共同父组件管理状态用 props/emit 传给两个子组件 -- script setup import { ref } from vue; const sharedData ref(null); const updateData (val) { sharedData.value val; }; /script template BrotherA :datasharedData updateupdateData / BrotherB :datasharedData updateupdateData / /template④ 父组件直接调子组件方法用 ref 但子组件没 expose!-- ❌ 父组件拿到 ref但调不到子组件方法 -- script setup import { ref } from vue; const childRef ref(null); const callChild () { childRef.value?.doSomething(); // ❌ 子组件没 expose拿到的是 undefined }; /script template Child refchildRef / /template!-- 子组件 -- script setup const doSomething () { /* ... */ }; // ❌ 没有 defineExpose外部访问不到 /script修复子组件用 defineExpose!-- ✅ 子组件暴露方法 -- script setup const doSomething () { /* ... */ }; const reset () { /* ... */ }; defineExpose({ doSomething, reset }); // ✅ 主动暴露 /script⑤ 全局状态该用 Pinia 却用了 provide/inject!-- ❌ 全局用户信息用 provide/inject刷新丢失、无法持久化 -- !-- App.vue -- script setup import { ref, provide } from vue; const user ref(null); provide(user, user); // ❌ 全局状态不该用 provide没法跨路由管理 /script修复用 Pinia// ✅ stores/user.jsimport{defineStore}frompinia;import{ref}fromvue;exportconstuseUserStoredefineStore(user,(){constuserref(null);constsetUser(val){user.valueval;};constlogout(){user.valuenull;};return{user,setUser,logout};});!-- ✅ 任意组件直接用 -- script setup import { useUserStore } from /stores/user; const userStore useUserStore(); // userStore.user, userStore.setUser(), userStore.logout() /script三、万能兜底按关系选方案组件关系首选方案备选方案父 → 子propsref expose子 → 父emit—祖先 → 后代provide/injectPinia兄弟状态提升EventBus / Pinia任意全局Pinia—临时事件总线mittPinia如果状态多了就迁移决策流程图需要通信的组件关系是什么 ├── 父子 → props emit ├── 祖先后代 → provide/inject ├── 兄弟 → 状态提升到共同父级 │ └── 父级太远→ Pinia └── 任意组件 → Pinia └── 只是临时事件→ mitt EventBus记得 off四、预防 checklist父子通信优先props emit不要用 ref 直接操作子组件除非表单聚焦等场景跨级传 prop超过 2 层就换provide/inject兄弟通信先考虑状态提升层级太深再上Pinia用 EventBus必须在 onUnmounted 里 off否则内存泄漏父组件 ref 调子组件方法子组件必须 defineExpose全局状态用户信息、主题、权限用 Pinia不要用 provide/injectprovide 传响应式数据时传 ref/computed别传普通值inject 时给默认值inject(key, defaultValue)防止找不到 provider五、一句话总结「组件通信按关系选方案」父子 props/emit跨级 provide/inject兄弟状态提升全局 Pinia。别简单通信套全局状态也别复杂状态硬透传 props。选对工具数据流才清晰最后问候亲爱的朋友们并邀请你们阅读我的全新著作 《Vue.js 3企业级项目开发实战微课视频版》