logo
Vue 入门:现代化的渐进式 JavaScript 框架

组件通信概述

在 Vue 应用中,组件是独立的代码单元,它们需要相互通信以构建完整的应用功能。Vue 提供了多种组件通信方式,每种方式都有其适用场景:

  1. Props Down:父组件向子组件传递数据
  2. Events Up:子组件向父组件发送消息
  3. Provide/Inject:跨层级组件通信
  4. Event Bus:任意组件间的通信
  5. Vuex/Pinia:全局状态管理

Props:父组件向子组件传递数据

基础用法

Props 验证

Vue 提供了丰富的 props 验证选项:

defineProps({
// 基础类型检查
propA: Number,
// 多种类型
propB: [String, Number],
// 必传
propC: {
type: String,
required: true
},
// 带默认值
propD: {
type: Number,
default: 100
},
// 对象默认值
propE: {
type: Object,
default: () => ({
message: 'hello'
})
},
// 自定义验证
propF: {
validator(value) {
return ['success', 'warning', 'danger'].includes(value)
}
}
})

Emit:子组件向父组件发送消息

基础用法

v-model 的使用

v-model 是 props 和 emit 的语法糖,用于实现双向绑定:

<!-- 父组件 -->
<template>
<custom-input v-model="searchText" />
</template>
<!-- 子组件 -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
</template>
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

Provide/Inject:跨层级组件通信

当需要从父组件向深层子组件传递数据时,可以使用 provide/inject:

使用 Symbol 作为 Key

为了避免命名冲突,可以使用 Symbol 作为 provide/inject 的 key:

keys.js
export const themeSymbol = Symbol('theme')
// 父组件
provide(themeSymbol, theme)
// 子组件
const theme = inject(themeSymbol)

事件总线:任意组件间通信

虽然 Vue 3 移除了内置的事件总线,但我们可以使用第三方库或自己实现一个简单的事件总线:

eventBus.js
import { ref } from 'vue'
class EventBus {
constructor() {
this.events = ref(new Map())
}
on(event, callback) {
if (!this.events.value.has(event)) {
this.events.value.set(event, [])
}
this.events.value.get(event).push(callback)
}
emit(event, data) {
if (this.events.value.has(event)) {
this.events.value.get(event).forEach(callback => callback(data))
}
}
off(event, callback) {
if (this.events.value.has(event)) {
const callbacks = this.events.value.get(event)
const index = callbacks.indexOf(callback)
if (index > -1) callbacks.splice(index, 1)
}
}
}
export default new EventBus()

使用事件总线:

// 组件 A
import eventBus from './eventBus'
eventBus.emit('userLoggedIn', { id: 1, name: 'Alice' })
// 组件 B
import eventBus from './eventBus'
eventBus.on('userLoggedIn', (user) => {
console.log('用户登录:', user)
})

最佳实践

  1. 选择合适的通信方式

    • 父子组件:优先使用 props/emit
    • 跨多层级:考虑 provide/inject
    • 全局状态:使用 Vuex/Pinia
    • 简单的全局通信:事件总线
  2. Props 的设计原则

    • 明确的命名
    • 适当的类型验证
    • 合理的默认值
    • 必要的文档注释
  3. 避免过度通信

    • 组件职责要单一
    • 避免过深的组件层级
    • 合理使用状态管理

总结

Vue 提供了丰富的组件通信方式,每种方式都有其适用场景:

  • Props/Emit:最基础、最常用的父子组件通信方式
  • Provide/Inject:适用于深层组件通信
  • 事件总线:适用于简单的全局通信
  • Vuex/Pinia:适用于复杂的状态管理

选择合适的通信方式对于构建可维护的 Vue 应用至关重要。在实际开发中,应根据具体场景选择最合适的通信方式,并遵循最佳实践原则。