文章目录一、前置基础知识1. 什么是组件通信2. 父子组件判定规则固定不变3. 原生事件 vs 自定义事件二、父子组件通信使用频率最高2.1 父组件向子组件传值defineProps适用场景完整代码示例补充要点2.2 子组件向父组件传值defineEmits 自定义事件两种实现思路核心流程完整代码示例2.3 父组件调用子组件方法/获取子组件数据ref defineExpose适用场景核心规则完整代码示例2.4 父子组件双向绑定v-model适用场景完整代码示例三、跨层级组件通信祖孙/隔多层组件3.1 $attrs 透传简单跨层级原理特点代码示例爷 → 父 → 孙3.2 provide / inject专属跨层级四、兄弟组件通信方案借助父组件中转执行流程适用场景五、全局任意组件通信无组件关系限制5.1 Pinia项目首选全局状态管理1. 安装2. 全局挂载main.js3. 定义全局仓库 store4. 任意组件使用5.2 Mitt 事件总线轻量发布订阅适用场景1. 安装2. 统一创建总线实例3. 四大核心 API4. 实战使用数据流向六、场景选型总结项目直接对照使用一句话口诀七、注意事项在 Vue3 开发中组件拆分是常态组件通信更是日常开发的基础技能。面对父子、兄弟、跨层级组件传值时总是容易混淆用法、踩坑不断。一、前置基础知识1. 什么是组件通信组件之间互相传递数据、调用方法统称为组件通信。Vue 项目几乎所有业务都离不开组件传值。2. 父子组件判定规则固定不变父组件通过import引入组件、在模板中使用组件标签的一方子组件被引入、被当作标签使用的一方注意组件不能互相import会造成循环引用直接报错3. 原生事件 vs 自定义事件这是新手高频混淆点重点区分原生事件如click、mouseenter、input等浏览器内置事件$event原生事件对象包含鼠标坐标、触发元素、按键信息等原生属性自定义事件开发者自定义事件名无固定规则$event等同于$emit传递的自定义数据可以是任意类型二、父子组件通信使用频率最高父子组件是项目中最常见的组件关系分为父传子、子传父、父调用子方法、双向绑定四类场景。2.1 父组件向子组件传值defineProps适用场景父组件把数据传递给子组件单向数据流。完整代码示例父组件template !-- 通过属性绑定向子组件传值 -- Child :msg父组件消息 :listarr / /template script setup import Child from ./Child.vue const arr [1, 2, 3] /script子组件接收数据script setup // 方式1基础类型校验推荐规范项目必用 const props defineProps({ msg: String, list: Array }) // 方式2简易写法仅快速接收不做类型校验 // const props defineProps([msg, list]) // 模板/逻辑中直接使用 props.xxx console.log(props.msg) /script template div{{ msg }}/div div{{ list }}/div /template补充要点数据流为单向子组件不要直接修改props数据会破坏数据流向、引发 Bug支持类型校验、默认值、必填校验适合团队协作规范约束2.2 子组件向父组件传值defineEmits 自定义事件两种实现思路主流方案子组件通过defineEmits派发自定义事件传值官方推荐间接方案父组件通过props传递回调函数子组件执行函数传参原理一致核心流程子组件触发动作 →emit派发事件携带数据 → 父组件监听同名事件 → 接收数据并执行逻辑完整代码示例子组件发送数据template button clicksendData向父组件传值/button /template script setup // 1. 定义可派发的自定义事件 const emit defineEmits([sendToParent]) // 2. 触发事件传递自定义数据 const sendData () { emit(sendToParent, 我是来自子组件的数据) } /script父组件接收数据template !-- 监听子组件派发的自定义事件 -- Child sendToParenthandleGetData / /template script setup // val 自动接收子组件传递过来的数据 const handleGetData (val) { console.log(父组件收到, val) } /script2.3 父组件调用子组件方法/获取子组件数据ref defineExpose适用场景父组件主动操作子组件调用子组件内部方法、读取子组件数据。核心规则script setup语法下组件内部属性和方法默认对外关闭必须通过defineExpose主动暴露父组件才能访问。完整代码示例父组件template Child refchildRef / button clickcallChildFn调用子组件方法/button /template script setup import { ref } from vue import Child from ./Child.vue // 定义 ref 关联子组件实例 const childRef ref() const callChildFn () { // 通过 ref.value 访问子组件暴露的方法/数据 childRef.value.childSay() console.log(childRef.value.childNum) } /script子组件script setup const childNum 666 const childSay () { alert(子组件方法被调用了) } // 主动暴露方法/数据给父组件 defineExpose({ childSay, childNum }) /script2.4 父子组件双向绑定v-model适用场景父子组件数据需要双向同步一方修改另一方自动更新。Vue3 中组件v-model本质是props 自定义事件的语法糖。完整代码示例父组件template !-- 双向绑定数据 -- Child v-modelinputValue / p父组件值{{ inputValue }}/p /template script setup import { ref } from vue import Child from ./Child.vue const inputValue ref() /script子组件template !-- 绑定 props 数据触发 update 事件更新数据 -- input :valuemodelValue inputhandleInput / /template script setup // 接收默认 propsmodelValue const props defineProps([modelValue]) // 声明更新事件update:modelValue const emit defineEmits([update:modelValue]) const handleInput (e) { // 触发事件同步数据到父组件 emit(update:modelValue, e.target.value) } /script三、跨层级组件通信祖孙/隔多层组件当组件层级较多爷-父-孙、多层嵌套一层层传props/emit代码冗余推荐两种方案$attrs透传、provide/inject。3.1 $attrs 透传简单跨层级原理上层组件传递的属性、自定义事件若中间组件不使用defineProps接收会自动存入$attrs中间组件通过v-bind$attrs一键透传给下层组件。特点中间组件零代码改造只做中转适合简单数据、事件透传不适合复杂全局状态代码示例爷 → 父 → 孙爷组件最上层template !-- 向子组件传递属性和事件 -- Father :info爷组件数据 toGrandpagetMsg / /template script setup const getMsg (val) { console.log(爷组件收到孙组件数据, val) } /script中间父组件中转层核心template !-- 整包透传 $attrs 到孙组件 -- Son v-bind$attrs / /template script setup // 不写 defineProps数据和事件自动进入 $attrs /script孙组件最下层script setup // 接收上层透传的数据 const props defineProps([info]) // 触发上层自定义事件 const emit defineEmits([toGrandpa]) const sendToGrandpa () { emit(toGrandpa, 孙组件的数据) } /script3.2 provide / inject专属跨层级Vue 官方专为多层嵌套跨层级传值设计的 API下文结合实战简述适合静态数据、配置类数据传递。补充provide/inject支持祖先组件向任意后代组件传值不受层级限制后代组件可直接注入使用。四、兄弟组件通信方案借助父组件中转兄弟组件没有直接关联通用思路A兄弟 → 父组件 → B兄弟。执行流程兄弟A 通过defineEmits触发自定义事件把数据传给父组件父组件接收数据通过defineProps把数据传给兄弟B适用场景简单兄弟传值层级少、逻辑简单的场景。复杂场景建议使用mitt或Pinia。五、全局任意组件通信无组件关系限制适用于任意页面、任意组件、任意层级的数据共享是中大型项目的核心方案分为两大主流工具Pinia、Mitt 事件总线。5.1 Pinia项目首选全局状态管理Vue 官方推荐状态管理库替代 Vuex轻量化、语法简洁90% 的项目全局数据都用它。适合场景用户信息、Token、购物车、全局配置等需要全局共享、频繁修改的数据。1. 安装npminstallpinia2. 全局挂载main.jsimport{createApp}fromvueimportAppfrom./App.vueimport{createPinia}frompiniaconstappcreateApp(App)app.use(createPinia())app.mount(#app)3. 定义全局仓库 store新建src/stores/test.jsimport{defineStore}frompiniaimport{ref}fromvue// 定义仓库唯一IDtestexportconstuseTestStoredefineStore(test,(){// 全局响应式数据constnumref(100)constchangeNum(val){num.valueval}// 对外暴露数据和方法return{num,changeNum}})4. 任意组件使用script setup import { storeToRefs } from pinia import { useTestStore } from /stores/test // 获取仓库实例 const testStore useTestStore() // 解构数据保持响应式 const { num } storeToRefs(testStore) // 直接调用仓库方法 testStore.changeNum(200) /script5.2 Mitt 事件总线轻量发布订阅Vue3 移除了原生$on/$off事件总线官方推荐使用mitt体积仅 200B、零依赖。适用场景兄弟组件、多层跨组件临时通信、全局消息通知不适合管理大型全局状态优先用 Pinia。1. 安装npmi mitt2. 统一创建总线实例新建src/utils/eventBus.jsimportmittfrommitt// 创建全局事件总线exportdefaultmitt()3. 四大核心 APIbus.on(事件名, 回调)监听事件接收数据bus.emit(事件名, 数据)触发事件发送数据bus.off(事件名, 回调)取消监听必写防止内存泄漏bus.all.clear()清空所有事件4. 实战使用发送数据的组件script setup import bus from /utils/eventBus const sendMsg () { // 触发事件并传值 bus.emit(globalMsg, 来自事件总线的消息) } /script接收数据的组件script setup import { onMounted, onBeforeUnmount } from vue import bus from /utils/eventBus // 抽离回调函数方便后续取消监听 const handleMsg (val) { console.log(接收数据, val) } // 组件挂载时监听事件 onMounted(() { bus.on(globalMsg, handleMsg) }) // 组件销毁时取消监听关键避免内存泄漏 onBeforeUnmount(() { bus.off(globalMsg, handleMsg) }) /script数据流向发送组件emit→ 全局总线中转 → 监听组件on接收回调六、场景选型总结项目直接对照使用通信场景推荐方案父组件 → 子组件 传值defineProps子组件 → 父组件 传值defineEmits自定义事件父组件调用子组件方法ref defineExpose父子数据双向同步v-model多层跨层级祖孙传值$attrs/provide/inject兄弟组件简单通信父组件中转全局组件共享状态Pinia首选临时跨组件消息通知Mitt 事件总线一句话口诀关系近用 props/emit层级多用透传全局共享直接上 Pinia七、注意事项禁止直接修改props数据遵循单向数据流使用mitt必须在组件销毁时off取消监听防止内存泄漏script setup中方法/属性默认不对外暴露父组件调用子组件必须配合defineExpose大型项目全局状态统一使用 Pinia不要混用多种通信方案降低维护成本。