文章目录
正文
1. Props/Emit 父子组件通信
1.1 Props 向下传递数据
Props 是 Vue 中最基本的组件通信方式,用于父组件向子组件传递数据。
// 子组件定义
export default {
name: 'ChildComponent',
props: {
// 基础类型检查
message: String,
// 多种类型
propA: [String, Number],
// 必填项
requiredProp: {
type: String,
required: true
},
// 带默认值
propWithDefault: {
type: Number,
default: 100
},
// 对象/数组默认值
objectProp: {
type: Object,
default: () => ({ key: 'value' })
},
// 自定义验证
customProp: {
validator(value) {
return ['success', 'warning', 'danger'].includes(value)
}
}
}
}
1.2 Emit 向上传递事件
子组件通过触发事件向父组件传递信息。
// 子组件
<template>
<div>
<button @click="sendToParent">发送到父组件</button>
</div>
</template>
<script>
export default {
emits: ['update', 'delete'], // 声明组件发出的事件
methods: {
sendToParent() {
// 触发事件并传递数据
this.$emit('update', { id: 1, name: '更新的数据' })
}
}
}
</script>
// 父组件
<template>
<child-component
@update="handleUpdate"
@delete="handleDelete"
/>
</template>
<script>
export default {
methods: {
handleUpdate(data) {
console.log('收到子组件数据:', data)
},
handleDelete(id) {
console.log('删除ID:', id)
}
}
}
</script>
2. EventBus 跨组件通信
2.1 创建事件总线
EventBus 允许任意组件间通信,不受组件层级限制。
// Vue 2 创建事件总线
// eventBus.js
import Vue from 'vue'
export const EventBus = new Vue()
// Vue 3 创建事件总线
// eventBus.js
import mitt from 'mitt'
export const EventBus = mitt()
2.2 使用事件总线
// 组件A - 发送事件
import { EventBus } from '@/eventBus'
export default {
methods: {
sendMessage() {
// Vue 2
EventBus.$emit('custom-event', { message: '这是一条消息' })
// Vue 3
EventBus.emit('custom-event', { message: '这是一条消息' })
}
}
}
// 组件B - 接收事件
import { EventBus } from '@/eventBus'
export default {
created() {
// Vue 2
EventBus.$on('custom-event', this.handleEvent)
// Vue 3
EventBus.on('custom-event', this.handleEvent)
},
beforeDestroy() { // Vue 2
EventBus.$off('custom-event', this.handleEvent)
},
beforeUnmount() { // Vue 3
EventBus.off('custom-event', this.handleEvent)
},
methods: {
handleEvent(data) {
console.log('收到事件数据:', data)
}
}
}
2.3 EventBus 优缺点
优点:
- 使用简单,可实现任意组件间通信
- 不需要组件间有直接的引用关系
缺点:
- 可能导致事件混乱,难以追踪数据流向
- 组件耦合度增加,不利于维护
- 大型应用中建议使用Vuex/Pinia等状态管理方案
3. Provide/Inject 深层组件通信
3.1 基本使用
适用于深层嵌套组件间通信,祖先组件提供数据,后代组件注入使用。
// 祖先组件提供数据
export default {
provide() {
return {
// 提供静态值
theme: 'dark',
// 提供响应式数据
user: this.user,
// 提供方法
updateUser: this.updateUser
}
},
data() {
return {
user: { name: '张三', role: 'admin' }
}
},
methods: {
updateUser(newUser) {
this.user = newUser
}
}
}
// 后代组件注入数据
export default {
inject: ['theme', 'user', 'updateUser'],
// 或者使用别名和默认值
inject: {
appTheme: {
from: 'theme',
default: 'light'
},
currentUser: 'user'
},
methods: {
changeUserRole() {
this.updateUser({...this.currentUser, role: 'editor'})
}
}
}
3.2 响应式处理
Vue 3中使用provide/inject实现响应式通信:
// 祖先组件 (Vue 3)
import { provide, ref, readonly } from 'vue'
export default {
setup() {
const count = ref(0)
function increment() {
count.value++
}
// 提供只读值防止子组件修改
provide('count', readonly(count))
provide('increment', increment)
return { count, increment }
}
}
// 后代组件 (Vue 3)
import { inject } from 'vue'
export default {
setup() {
const count = inject('count')
const increment = inject('increment')
return { count, increment }
}
}
3.3 Provide/Inject 优缺点
优点:
- 解决深层嵌套组件通信问题
- 避免了"prop drilling"(属性透传)
- Vue 3中与组合式API结合使用更加灵活
缺点:
- 增加了组件间的隐式依赖
- 重构时可能导致问题
- 数据来源不明确,可能影响代码可维护性
4. 各通信方式对比与选择
通信方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
Props/Emit | 父子组件通信 | 简单直接,Vue官方推荐 | 层级深时需要多层传递 |
EventBus | 任意组件间通信 | 使用简单,无需组件关系 | 事件难以追踪,大型应用不推荐 |
Provide/Inject | 深层组件通信 | 避免属性透传 | 增加隐式依赖 |
Vuex/Pinia | 复杂应用状态管理 | 集中管理状态,状态变化可追踪 | 小型应用可能过于复杂 |
a t t r s / attrs/ attrs/listeners | 透传属性和事件 | 无需显式声明即可传递 | 仅适用于中间层组件传递 |
5. 最佳实践建议
- 就近原则:优先使用最简单的通信方式解决问题
- 明确数据流:保持单向数据流,便于追踪和调试
- 合理拆分组件:减少不必要的组件嵌套和通信
- 状态提升:将共享状态提升到最近的共同父组件
- 大型应用:考虑使用Vuex/Pinia进行状态管理
- 文档化:为组件间的通信方式编写清晰的文档
通过合理选择和组合这些通信方式,可以构建出数据流清晰、易于维护的Vue应用。
结语
感谢您的阅读!期待您的一键三连!欢迎指正!