本文系统梳理 Vue 组件通信的完整方案,从 Props、$emit、v-model 到 provide/inject、Event Bus 与 Pinia,涵盖...
快速概述
Vue 组件通信解决的是组件之间数据传递与状态同步的核心问题,适用于父子组件、兄弟组件以及跨层级组件的场景。前置环境要求 Vue 3.x 或 Vue 2.7+,推荐使用 script setup 语法糖与组合式 API。根据通信方向与复杂度,可选用 Props、$emit、v-model、ref、provide/inject、Event Bus、Pinia 及 attrs 透传等方案。
核心概念与原理
组件通信的本质是数据流的单向或双向绑定。Props 实现父到子的单向数据流,$emit 让子组件向父组件发送事件,v-model 则封装了 props 与 emit 实现双向绑定。provide/inject 允许祖先组件向后代组件注入依赖,无需逐层传递。Event Bus 通过事件发布订阅实现任意组件通信,而 Pinia 作为全局状态管理库,提供集中式存储与响应式更新。
实战步骤与代码
1. Props 父传子
// 父组件
<Child :message="parentMsg" />
// 子组件
const props = defineProps({ message: String })
2. $emit 子传父
// 子组件触发事件
const emit = defineEmits(['call-dad'])
emit('call-dad', 'Hello')
// 父组件监听
<Child @call-dad="handleCall" />Vue 3 推荐使用 defineEmits 声明事件,提升类型安全。
3. v-model 双向绑定
// 父组件
<Child v-model="formData.name" />
// 子组件
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
4. ref 调用子组件方法
// 父组件获取子组件实例
const childRef = ref(null)
childRef.value.sayHello()注意:过度使用 ref 会破坏组件封装性。
5. provide/inject 跨层级
// 祖先组件
provide('theme', 'dark')
// 后代组件
const theme = inject('theme')适用于主题、配置等跨层级数据传递。
6. Event Bus 任意组件
import mitt from 'mitt'
const bus = mitt()
bus.emit('notice', '停水通知')
bus.on('notice', (msg) => console.log(msg))适合小型项目全局通知,但需注意事件管理。
7. Pinia 全局状态
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => ({ count: 0 })
})
const store = useStore()
store.count++Pinia 对 TypeScript 友好,可替代 Vuex 作为全局状态管理方案。
常见坑点与优化
- Props 单向数据流:子组件不应直接修改 props,应通过 emit 通知父组件更新。
- Event Bus 事件泄漏:组件销毁时需移除监听,避免内存泄漏与事件混乱。
- provide/inject 响应性:注入的 ref 或 reactive 对象保持响应性,但普通值不会自动更新。
- 性能优化:避免深层嵌套传递 props,优先使用 provide/inject 或 Pinia 减少中间层透传。
- attrs 透传:使用 v-bind="$attrs" 传递未声明属性,适合高阶组件封装。
兄弟组件通信可借助共同父组件中转,或直接使用 Event Bus / Pinia 简化逻辑。
本站文章除特别声明外,均由本站原创,并采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行授权。转载请务必保留本段声明及完整超链接。
评论列表 0