当前位置: 首页 > news >正文

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

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

一句话总结:父子用 props/emit,跨级用 provide/inject,兄弟用状态提升或 EventBus,全局用 Pinia。按关系选方案,别上来就 Vuex/Pinia!


正文目录

  1. Vue 组件通信全景图
  2. 5 大高频场景 & 最佳实践
  3. 万能兜底:按关系选方案
  4. 预防 checklist(不再踩坑)
  5. 一句话总结

一、Vue 组件通信全景图

Vue 组件通信的本质就是:数据怎么从 A 传到 B,B 怎么通知 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>
② 跨级通信:层层透传 props(prop drilling)
<!-- ❌ 中间组件不需要这个 prop,只是透传 --> <!-- Grandfather.vue --> <Template :theme="theme" /> <!-- Father.vue (不需要 theme,但必须接) --> <script setup> defineProps(['theme']); // ❌ 纯透传 </script> <template> <Child :theme="theme" /> </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 方案,组件销毁时没移除监听 → 内存泄漏importmittfrom'mitt';constbus=mitt();// BrotherA.vuebus.emit('refresh',data);// BrotherB.vuebus.on('refresh',handler);// ❌ 没有 bus.off,切走后还在监听

修复方案一:onUnmounted 时移除监听

// ✅ 组件销毁时移除import{onUnmounted}from'vue';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 :data="sharedData" @update="updateData" /> <BrotherB :data="sharedData" @update="updateData" /> </template>
④ 父组件直接调子组件方法:用 ref 但子组件没 expose
<!-- ❌ 父组件拿到 ref,但调不到子组件方法 --> <script setup> import { ref } from 'vue'; const childRef = ref(null); const callChild = () => { childRef.value?.doSomething(); // ❌ 子组件没 expose,拿到的是 undefined }; </script> <template> <Child ref="childRef" /> </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}from'pinia';import{ref}from'vue';exportconstuseUserStore=defineStore('user',()=>{constuser=ref(null);constsetUser=(val)=>{user.value=val;};constlogout=()=>{user.value=null;};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/inject
  • provide 传响应式数据时传 ref/computed,别传普通值
  • inject 时给默认值inject('key', defaultValue),防止找不到 provider

五、一句话总结

「组件通信按关系选方案」:父子 props/emit,跨级 provide/inject,兄弟状态提升,全局 Pinia。别简单通信套全局状态,也别复杂状态硬透传 props。选对工具,数据流才清晰!


最后问候亲爱的朋友们,并邀请你们阅读我的全新著作

📚 《Vue.js 3企业级项目开发实战(微课视频版》

http://www.jsqmd.com/news/1293160/

相关文章:

  • VPC网络
  • 2026武汉代理记账公司哪家靠谱?收费标准、避坑要点与6家本土正规机构汇总 - 品牌智鉴榜
  • 2026 年溧之星汽车维修|溧水区汽车常见故障与应急处理养护攻略 - 国麟测评
  • AI 时代:基于 DDD 搭建可供 AI Agent 调用的 MES/WMS/QMS 一体化架构
  • Sunshine游戏串流:如何将你的高性能PC变成全家共享的云游戏中心?
  • 无犯罪记录证明公证去哪儿办?办理无犯罪记录公证需要什么材料? - 叮咚办真方便
  • 2026江镇装修干货:挑公司就看这5条 - 地大物博的游客
  • Qt窗口尺寸固定:禁止拖拽的3种实现方案与跨平台避坑指南
  • 2026年AI数据分析软件推荐:洞察与报告对比
  • CANN算子生态:基础与安全算子的协同架构设计
  • 位运算 算法的学习与习题
  • 盐城本地环保定制服务干货分享 适配多类场景需求省心又靠谱 - 热点速览
  • 专职消防驻站服务如何选?皇甲消防“防消联动”模式提供实践样本 - 天下见闻
  • 蒜香蘸料厂商 - 热点速览
  • 涡轮增压改装指南:从原理到实战,解决动力与油耗平衡
  • 武汉世达实用外国语学校2026年报名咨询指南 - 武汉中职最新信息发布
  • 培训机构智能排课系统哪个最好用?主流工具功能介绍+高效排课技巧全梳理 - 品牌测评鉴赏家
  • LBP与AdaBoost算法在行人检测中的应用与优化
  • 用 CDS 注解塑造 SAP Fiori 表格,让一份数据模型拥有多套界面布局
  • 2026乌鲁木齐克拉玛依吐鲁番膜结构车棚张拉膜停车棚指南 - LYL仔仔
  • 深入解析Spring Security认证授权流程:从核心组件到实战扩展
  • Xbox 艰难季度后宣布“重启”计划,微软预计 2027 财年恢复增长
  • 2026 石家庄长安区防水补漏权威攻略:卫生间免砸砖堵漏、屋面防渗、外墙修漏、阳台防水正规施工 + 明细报价 - 超人防水
  • 3步快速恢复QQ空间历史数据:GetQzonehistory完整指南
  • TranslucentTB终极指南:5分钟让Windows任务栏变透明的高效美化工具
  • 2026株洲CAAC执照培训报名机构推荐 - 谁都没有我好看
  • 2026年一站式加工定制机箱钣金:三大核心趋势 - 全域品牌推荐
  • React 中 useMemo 和 useCallback 什么时候该用什么时候不该用?
  • 2026技术趋势与职业规划方法论
  • 鸣潮自动化工具ok-ww:零门槛解放双手,智能完成日常任务的完整指南