一、组件通信基础:单向数据流(Props)
在Vue3的单向数据流架构中,父组件通过props向子组件传递数据是最基础的通信方式。这种设计遵循”单向数据流”原则,确保数据变更可追溯。
1.1 基础实现
<!-- 父组件 --><template><ChildComponent :title="pageTitle" :count="itemCount" /></template><script setup>import { ref } from 'vue'const pageTitle = ref('用户管理')const itemCount = ref(100)</script><!-- 子组件 --><template><div><h2>{{ title }}</h2><p>总数:{{ count }}</p></div></template><script setup>const props = defineProps({title: String,count: {type: Number,default: 0}})</script>
1.2 类型验证与默认值
Vue3推荐使用对象语法定义props,支持完整的类型检查:
defineProps({// 基础类型检查status: Boolean,// 多个可能类型size: {type: String,validator: (value) => ['small', 'medium', 'large'].includes(value)},// 复杂对象user: {type: Object,default: () => ({ name: 'Guest', role: 'visitor' })}})
1.3 最佳实践
- 避免直接修改props(会触发Vue警告)
- 对于复杂对象,使用函数返回默认值防止引用问题
- 大型项目建议配合TypeScript使用
二、反向通信:自定义事件(Emits)
当子组件需要通知父组件时,通过自定义事件实现反向通信。
2.1 基础实现
<!-- 父组件 --><template><ChildComponent @submit-form="handleSubmit" /></template><script setup>const handleSubmit = (formData) => {console.log('收到表单数据:', formData)}</script><!-- 子组件 --><template><form @submit.prevent="$emit('submit-form', formData)"><!-- 表单内容 --></form></template><script setup>const formData = { username: '', password: '' }const emit = defineEmits(['submit-form'])</script>
2.2 事件验证
Vue3支持对emit事件进行验证:
const emit = defineEmits({// 无验证click: null,// 带验证submit: (payload) => {if (payload && typeof payload === 'object') {return true}console.warn('Invalid submit event payload!')return false}})
2.3 事件命名规范
- 使用kebab-case命名(如
update-data) - 避免与原生HTML事件冲突
- 语义化命名(如
fetch-success而非onSuccess)
三、双向绑定:v-model进阶
Vue3对v-model进行了重大改进,支持多个双向绑定和自定义参数。
3.1 单个v-model
<!-- 父组件 --><template><CustomInput v-model="searchText" /></template><!-- 子组件 --><template><input:value="modelValue"@input="$emit('update:modelValue', $event.target.value)"/></template><script setup>defineProps(['modelValue'])defineEmits(['update:modelValue'])</script>
3.2 多个v-model绑定
Vue3支持为不同属性绑定多个v-model:
<!-- 父组件 --><template><UserProfilev-model:first-name="firstName"v-model:last-name="lastName"/></template><!-- 子组件 --><template><div><input :value="firstName" @input="$emit('update:firstName', $event.target.value)"><input :value="lastName" @input="$emit('update:lastName', $event.target.value)"></div></template><script setup>defineProps(['firstName', 'lastName'])defineEmits(['update:firstName', 'update:lastName'])</script>
3.3 defineModel实验特性
Vue3.3+提供的defineModel简化了双向绑定实现:
<script setup>// 子组件const model = defineModel({ default: '' })</script><!-- 等效于 --><script setup>const props = defineProps(['modelValue'])const emit = defineEmits(['update:modelValue'])const model = computed({get: () => props.modelValue,set: (value) => emit('update:modelValue', value)})</script>
四、状态管理:Pinia核心方案
对于跨组件状态共享,Pinia是当前推荐的状态管理方案。
4.1 基础Store定义
// stores/counter.jsimport { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', {state: () => ({count: 0,lastUpdated: null}),getters: {doubleCount: (state) => state.count * 2},actions: {increment() {this.count++this.lastUpdated = new Date()},async fetchCount() {const res = await fetch('/api/count')this.count = await res.json()}}})
4.2 组件中使用
<template><div><p>Count: {{ counter.count }}</p><p>Double: {{ counter.doubleCount }}</p><button @click="counter.increment">Increment</button></div></template><script setup>import { useCounterStore } from '@/stores/counter'const counter = useCounterStore()</script>
4.3 高级特性
4.3.1 持久化插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia()pinia.use(piniaPluginPersistedstate)// Store配置export const useUserStore = defineStore('user', {state: () => ({ /* ... */ }),persist: {enabled: true,strategies: [{key: 'auth_user',storage: localStorage,paths: ['token', 'userInfo']}]}})
4.3.2 模块化组织
stores/├── auth/│ ├── index.js # 主store│ ├── user.js # 用户模块│ └── permissions.js # 权限模块├── ui/│ └── theme.js # 主题配置└── app.js # 应用全局状态
4.4 与Vuex对比优势
| 特性 | Pinia | Vuex 4 |
|---|---|---|
| 类型支持 | 原生TypeScript支持 | 需要额外配置 |
| 模块化 | 自动模块拆分 | 需手动注册模块 |
| API设计 | 更简洁的Composition API | Options API |
| 插件系统 | 更灵活的插件机制 | 相对固定 |
| 开发体验 | 更好的DevTools集成 | 基础支持 |
五、方案选择指南
根据不同场景选择合适的通信方式:
| 场景 | 推荐方案 | 复杂度 | 适用范围 |
|---|---|---|---|
| 父子简单数据传递 | props/emits | ★☆☆ | 基础组件通信 |
| 表单双向绑定 | v-model | ★★☆ | 表单类组件 |
| 跨组件状态共享 | Pinia | ★★★ | 全局状态管理 |
| 复杂业务逻辑交互 | 事件总线(或Pinia) | ★★★★ | 大型应用组件通信 |
六、性能优化建议
- 避免过度通信:对于频繁更新的数据,考虑使用防抖/节流
- 合理拆分Store:将独立状态拆分为不同Store
- 选择性状态持久化:仅持久化必要数据
- 使用计算属性:减少重复计算
- 大型应用考虑模块化:按功能划分Pinia模块
通过系统掌握这些组件通信方案,开发者可以构建出结构清晰、可维护性强的Vue3应用。在实际开发中,建议根据项目规模和复杂度选择合适的通信策略,通常小型项目使用props/emits即可,中大型项目建议结合Pinia进行状态管理。