Vue3 组件通信全方案(10种写法+详细实例)
Vue3 组件通信分父子、兄弟、跨级/全局 三大场景,我把所有常用+官方推荐的写法整理好了,带完整可运行代码,直接复制就能用。
一、父子组件通信(最常用 6 种)
1. props / emit(标准写法:父传子 + 子传父)
适用场景:父 ↔ 子 双向通信(Vue 最基础、最推荐)
父组件 Parent.vue
vue
<template>
<div>
<h2>父组件</h2>
<!-- 父传子:msg -->
<!-- 子传父:@sendMsg 监听子组件事件 -->
<Child :msg="parentMsg" @sendMsg="getChildMsg" />
<p>子组件传来的消息:{{ childMsg }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
// 父传给子的数据
const parentMsg = ref('我是父组件的消息')
// 接收子组件的数据
const childMsg = ref('')
// 监听子组件触发的事件
const getChildMsg = (val) => {
childMsg.value = val
}
</script>
子组件 Child.vue
vue
<template>
<div>
<h3>子组件</h3>
<p>父组件传来的消息:{{ msg }}</p>
<button @click="toParent">点击给父组件传值</button>
</div>
</template>
<script setup>
// 1. 接收父组件传值:props
const props = defineProps({
msg: {
type: String,
default: ''
}
})
// 2. 向父组件传值:emit
const emit = defineEmits(['sendMsg'])
const toParent = () => {
// 触发事件,把值传给父组件
emit('sendMsg', '我是子组件的消息')
}
</script>
2. v-model(双向绑定,语法糖)
适用场景:表单、弹窗显隐、需要双向同步的数据
父组件
vue
<template>
<Child v-model="value" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const value = ref('')
</script>
子组件
vue
<template>
<input
:value="modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>
<script setup>
// v-model 默认参数:modelValue
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
自定义 v-model 名称(多个双向绑定):
vue
<!-- 父 -->
<Child v-model:name="name" v-model:age="age" />
<!-- 子 -->
defineProps(['name', 'age'])
defineEmits(['update:name', 'update:age'])
3. ref / $parent(父直接获取子实例)
适用场景 :父组件直接调用子组件方法 / 数据
父组件
vue
<template>
<Child ref="childRef" />
<button @click="getChild">调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
// 获取子组件实例
const childRef = ref(null)
const getChild = () => {
// 调用子组件方法
childRef.value.sayHello()
// 获取子组件数据
console.log(childRef.value.childMsg)
}
</script>
子组件
vue
<script setup>
import { ref } from 'vue'
const childMsg = ref('我是子组件数据')
// 方法必须暴露,父组件才能访问
const sayHello = () => {
alert('子组件方法被调用了')
}
// 关键:暴露属性/方法给父组件
defineExpose({
childMsg,
sayHello
})
</script>
4. provide / inject(跨层级父子:爷 → 孙)
适用场景:父/爷组件 → 深层子组件传值(跨级)
顶层组件(爷/父)
vue
<script setup>
import { provide, ref } from 'vue'
const msg = ref('我是顶层组件数据')
// 提供数据
provide('globalMsg', msg)
</script>
深层子组件(孙/曾孙)
vue
<script setup>
import { inject } from 'vue'
// 注入数据
const msg = inject('globalMsg')
console.log(msg) // 顶层组件数据
</script>
✅ 响应式传递 :provide 传递 ref/reactive 对象,子孙修改会同步更新。
5. 插槽传值(子 → 父:作用域插槽)
适用场景 :子组件把数据传给父组件的插槽内容
子组件 Child.vue
vue
<template>
<div>
<!-- 子组件把数据传给插槽 -->
<slot :childData="childData" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const childData = ref('子组件插槽数据')
</script>
父组件
vue
<template>
<Child>
<!-- 接收子组件插槽数据 -->
<template v-slot="slotProps">
{{ slotProps.childData }}
</template>
</Child>
</template>
6. $attrs(透传属性,爷 → 孙)
适用场景:中间组件不处理,直接把属性/事件传给子组件
父组件
vue
<Child title="我是标题" @click="handleClick" />
中间组件(Middle.vue)
vue
<template>
<!-- 所有属性/事件直接透传给孙组件 -->
<GrandChild v-bind="$attrs" />
</template>
<script setup>
import GrandChild from './GrandChild.vue'
</script>
孙组件
vue
<script setup>
// 接收透传的属性
const props = defineProps(['title'])
</script>
二、兄弟组件通信(2 种)
1. 父组件中转(最简单)
思路:兄 → 父 → 弟
vue
<!-- 父组件作为中转站 -->
<BrotherA @send="val => brotherMsg = val" />
<BrotherB :msg="brotherMsg" />
2. mitt(事件总线,Vue3 官方推荐)
Vue3 移除了 $bus,用 mitt 代替全局事件通信
1)安装 mitt
bash
npm install mitt
2)创建工具文件 eventBus.js
js
import mitt from 'mitt'
export default mitt()
3)兄弟A(发送方)
vue
<script setup>
import bus from './eventBus.js'
// 发送事件
const send = () => {
bus.emit('toBrother', '我是兄弟A的数据')
}
</script>
4)兄弟B(接收方)
vue
<script setup>
import bus from './eventBus.js'
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
// 监听事件
bus.on('toBrother', (val) => {
console.log(val)
})
})
// 组件销毁时移除监听
onUnmounted(() => {
bus.off('toBrother')
})
</script>
三、全局/跨页面通信(2 种)
1. Pinia(Vue 官方状态管理,首选)
适用场景:全局共享数据(用户信息、主题、购物车)
1)安装
bash
npm install pinia
2)创建 store
js
// stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: '张三'
}),
actions: {
setName(name) {
this.name = name
}
}
})
3)任意组件使用
vue
<script setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 获取数据
console.log(userStore.name)
// 修改数据
userStore.setName('李四')
</script>
2. localStorage / sessionStorage
适用场景:页面刷新后仍需要保留的数据
js
// 存
localStorage.setItem('msg', '全局数据')
// 取
localStorage.getItem('msg')
// 删
localStorage.removeItem('msg')
快速选型指南(直接背)
| 通信场景 | 推荐方案 | 优先级 |
|---|---|---|
| 父 ↔ 子 | props / emit | 1 |
| 父子双向绑定 | v-model | 2 |
| 父调用子方法 | ref + defineExpose | 3 |
| 跨级父子 | provide / inject | 4 |
| 兄弟组件 | mitt / 父中转 | 5 |
| 全局共享 | Pinia | 1 |
| 页面刷新保留 | localStorage | - |
总结
- 父子通信 :优先
props/emit、v-model;跨级用provide/inject - 兄弟通信 :简单用父中转,复杂用
mitt - 全局通信 :必须用 Pinia(Vue3 官方标准)
- 所有代码都是 Vue3 + script setup 最新语法,可直接在项目中运行