以下是 Vue 组件通信的三种核心方式及详细实现方案,适用于 Vue 2/3 版本:
1. 父子组件通信
核心方法:Props 向下传递,Events 向上传递
vue
<!-- 父组件 Parent.vue -->
<template>
<Child
:title="parentData"
@child-event="handleEvent"
/>
</template>
<script>
export default {
data() {
return { parentData: "来自父组件的数据" }
},
methods: {
handleEvent(payload) {
console.log("子组件传值:", payload) // 接收子组件数据
}
}
}
</script>
<!-- 子组件 Child.vue -->
<template>
<div>
<h2>{{ title }}</h2> <!-- 接收父组件数据 -->
<button @click="sendToParent">传递数据</button>
</div>
</template>
<script>
export default {
props: ['title'], // 声明接收的prop
methods: {
sendToParent() {
this.$emit('child-event', { data: "子组件数据" }) // 触发自定义事件
}
}
}
</script>
关键点:
-
Prop 验证 (Vue 2/3 通用):
jsprops: { title: { type: String, required: true, default: "默认标题" } }
-
Vue 3 特有 :使用
defineProps
和defineEmits
编译器宏(<script setup>
)vue<script setup> const props = defineProps(['title']) const emit = defineEmits(['child-event']) emit('child-event', { data: "子组件数据" }) </script>
2. 插槽(Slots)
用于内容分发,支持模板级通信
vue
<!-- 父组件 -->
<Child>
<!-- 默认插槽 -->
<template>默认插入内容</template>
<!-- 具名插槽 -->
<template v-slot:header>
<h1>标题插槽</h1>
</template>
<!-- 作用域插槽(接收子组件数据) -->
<template v-slot:footer="slotProps">
<p>来自子组件的数据: {{ slotProps.childData }}</p>
</template>
</Child>
<!-- 子组件 Child.vue -->
<template>
<div>
<slot name="header"></slot> <!-- 具名插槽 -->
<slot></slot> <!-- 默认插槽 -->
<slot name="footer" :childData="dataForParent"></slot> <!-- 作用域插槽 -->
</div>
</template>
<script>
export default {
data() {
return { dataForParent: "子组件传递的数据" }
}
}
</script>
关键点:
- 动态插槽名 :
<template v-slot:[dynamicSlotName]>
- 缩写语法 :
#header
等价于v-slot:header
- 作用域插槽是子组件向父组件模板传递数据的唯一方式
3. 非父子组件通信
方案 1:事件总线(Event Bus)
js
// eventBus.js (Vue 2)
import Vue from 'vue'
export default new Vue()
// 组件A(发送事件)
import eventBus from './eventBus'
eventBus.$emit('global-event', { data: "跨组件数据" })
// 组件B(接收事件)
eventBus.$on('global-event', payload => {
console.log("收到数据:", payload)
})
Vue 3 替代方案(推荐使用 mitt 库):
bash
npm install mitt
js
// eventBus.js
import mitt from 'mitt'
export default mitt()
// 使用方式相同(取消 .$ 前缀)
eventBus.emit('event')
eventBus.on('event', callback)
方案 2:Provide / Inject(依赖注入)
vue
<!-- 祖先组件 -->
<script>
export default {
provide() {
return {
sharedData: "全局数据",
sharedMethod: this.doSomething // 传递方法
}
},
methods: {
doSomething() { console.log("跨组件调用") }
}
}
</script>
<!-- 后代组件(任意层级) -->
<script>
export default {
inject: ['sharedData', 'sharedMethod'],
mounted() {
console.log(this.sharedData) // "全局数据"
this.sharedMethod() // 调用祖先方法
}
}
</script>
Vue 3 增强:
vue
<!-- 祖先组件(Composition API) -->
<script setup>
import { provide, ref } from 'vue'
const counter = ref(0)
provide('counter', counter) // 提供响应式数据
</script>
<!-- 后代组件 -->
<script setup>
import { inject } from 'vue'
const counter = inject('counter')
</script>
方案 3:状态管理库(Vuex / Pinia)
js
// Pinia 示例 (Vue 3 推荐)
// store/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() { this.count++ }
}
})
// 任意组件中使用
import { useCounterStore } from '@/store/counter'
const store = useCounterStore()
console.log(store.count) // 获取状态
store.increment() // 触发动作
通信方案对比
方式 | 适用场景 | 数据流向 | 复杂度 |
---|---|---|---|
Props/Events | 直接父子组件 | 双向 | ⭐ |
Slots | 内容分发+模板数据传递 | 子→父(模板) | ⭐⭐ |
Event Bus | 简单跨组件通信(小型应用) | 任意方向 | ⭐⭐ |
Provide/Inject | 深层嵌套组件 | 祖先→后代 | ⭐⭐⭐ |
Vuex/Pinia | 中大型应用状态管理 | 集中式 | ⭐⭐⭐⭐ |
最佳实践建议:
- 优先使用
Props/Events
处理直接父子通信- 深层组件用
Provide/Inject
替代多级 prop 传递- 全局状态管理首选 Pinia (Vue 3) 或 Vuex
- 事件总线仅适用于简单场景,中大型项目慎用