Vue 组件间通信有哪些方式?
Vue 组件间的通信方式非常丰富,适用于不同的场景(父子、兄弟、跨级、全局)。为了方便记忆和理解,我们可以将其按组件关系和适用场景进行分类。
以下是 Vue 中主流的通信方式总结:
1. 父子组件通信
这是最基础、最常用的通信方式。
A. Props (父传子)
- 描述:父组件通过属性绑定的方式将数据传递给子组件,子组件通过
props接收。 - 适用:单向数据流,父组件向子组件传递数据。
- 代码简述:html
<!-- 父组件 --> <Child :msg="message" /> <!-- 子组件 --> <script setup> defineProps(['msg']) </script>
B. $emit (子传父)
- 描述:子组件通过触发自定义事件,将数据作为参数传递给父组件。
- 适用:子组件通知父组件改变数据或执行操作。
- 代码简述:js
// 子组件 const emit = defineEmits(['update']) emit('update', data) // 父组件 <Child @update="handleUpdate" />
C. v-model (双向绑定)
- 描述:本质是
props+emit的语法糖。 - Vue 2:使用
valueprop 和input事件(或.sync修饰符)。 - Vue 3:默认使用
modelValueprop 和update:modelValue事件。支持多个 v-model。 - 适用:表单组件或需要双向同步数据的组件。
D. $refs / defineExpose (父访子)
- 描述:父组件通过
ref获取子组件实例,直接调用子组件的方法或访问数据。 - 注意:在 Vue 3
<script setup>中,子组件默认是关闭的,必须通过defineExpose暴露属性或方法,父组件才能访问。 - 适用:父组件需要主动触发子组件的某个功能(如:打开弹窗、重置表单)。
2. 跨级组件通信 (祖先与后代)
当组件嵌套层级较深,中间组件不需要数据时,使用 Props 逐层传递(Props Drilling)会很麻烦。
A. Provide / Inject (依赖注入)
- 描述:祖先组件使用
provide提供数据,任意深度的后代组件使用inject接收数据。 - 特点:默认不是响应式的,如果需要响应式,传递的数据必须是
ref或reactive对象。 - 适用:深层嵌套组件、编写插件或组件库。
B. $attrs (透传 Attributes)
- 描述:包含了父作用域中不作为
props或emits被识别的 attribute 绑定(class, style, id 等)。 - Vue 3 变化:Vue 2 中的
$listeners已被合并到$attrs中。 - 适用:封装高阶组件,或者将属性“透传”给内部的根元素。
3. 兄弟组件或任意组件通信
A. Event Bus (事件总线)
- 描述:创建一个全局空的 Vue 实例作为事件中心,通过
$on监听,$emit触发,$off销毁。 - Vue 3 现状:Vue 3 移除了
$on,$off,$once实例方法,官方不再支持自带的 Event Bus。 - 替代方案:在 Vue 3 中推荐使用第三方库,如
mitt或tiny-emitter。 - 适用:轻量级的跨组件通信,项目规模较小。
B. Vuex / Pinia (状态管理)
- 描述:集中式存储管理应用的所有组件的状态。
- 现状:Pinia 是 Vue 3 官方推荐的状态管理库(Vuex 5 的精神继任者),比 Vuex 更轻量、API 更友好(去掉了 mutation,支持 Composition API)。
- 适用:中大型项目,多个组件共享状态,逻辑复杂。
4. 其他方式 (较少用或特定场景)
$parent/$root:- 子组件可以直接访问父组件实例或根实例。
- 缺点:耦合度太高,难以维护,通常不推荐使用。
- 浏览器本地存储 (localStorage / sessionStorage):
- 数据持久化,不同页面或组件间共享数据,但不是响应式的(需要配合监听 storage 事件或自定义 hook)。
- Window 全局变量:
- 最原始的方式,极不推荐,容易造成命名冲突和难以追踪的数据流。
总结:如何选择?
| 场景 | 推荐方式 | 备注 |
|---|---|---|
| 父 子 | Props |
最标准方式 |
| 子 父 | $emit |
最标准方式 |
| 父 子 | v-model |
双向绑定首选 |
| 父调用子方法 | ref + defineExpose |
比如控制弹窗显示 |
| 祖先 后代 | Provide / Inject |
避免 Props 层层传递 |
| 兄弟/全局 (简单) | Event Bus (mitt) |
小项目 |
| 兄弟/全局 (复杂) | Pinia (或 Vuex) | 大项目首选 |
| 组件库封装 | $attrs |
透传属性 |
右滑查看面试常问