第 24 题:Vue3 的组件通信方式(props / emit / v-model / provide-inject / expose / eventBus

111 阅读3分钟

第 24 题:Vue3 的组件通信方式(props / emit / v-model / provide-inject / expose / eventBus / store)


🎯 一、Vue3 组件通信方式总结表(可背)

通信方式方向适用场景是否响应式
props父 → 子传值、配置
emit子 → 父通知事件、返回数据N/A(事件)
v-model双向(父 ↔ 子)表单 / 双向绑定组件
provide / inject祖先 → 后代跨层级通信、通用组件默认非响应式(可用 ref)
expose子 → 父(实例 API)暴露内部方法供父调用N/A
EventBus任意 ↔ 任意小型项目的全局事件依赖实现
Pinia / Vuex全局共享大型项目、复杂业务✅ 强响应式

🎯 二、各通信方式详解


1️⃣ Props:父 → 子(最核心通信方式)

示例

<Child :msg="parentMsg" />

子组件

defineProps({
  msg: String
})

适用场景

  • 父组件传值给子组件
  • 表单项、配置项、初始化数据

原理

编译模板时,props 变成组件的属性,最终映射到组件实例上。
Vue3 对 props 做 shallowReadonly,防止子组件修改。


2️⃣ Emit:子 → 父

子组件:

const emit = defineEmits(['update'])
emit('update', newVal)

父组件:

<Child @update="handleUpdate" />

适合:事件通知、按钮点击回调等。


3️⃣ v-model(双向通信)

Vue3 支持多个 v-model:

父:

<Child v-model:title="name" />

子:

const props = defineProps(['title'])
const emit = defineEmits(['update:title'])

emit('update:title', newVal)

本质:props + emit 的语法糖


4️⃣ provide / inject(跨层级通信)

祖先组件:

provide('theme', ref('dark'))

后代组件:

const theme = inject('theme')

特点

  • 跨层级通信(不用 props 逐层传递)
  • 默认非响应式,如需响应式必须提供 ref/reactive

原理(常考)

provide 在组件创建时写入 “依赖注入表”,
inject 在后代组件创建时从原型链查找依赖。


5️⃣ expose(子组件暴露方法给父组件)

Vue3 新增的,非常常用!

子组件:

const count = ref(0)
function reset() {
  count.value = 0
}

defineExpose({
  reset
})

父组件:

<Child ref="childRef" />

childRef.value.reset()

适用:表单校验、重置、打开弹窗等。

Vue3 默认 setup 中的方法不会暴露给父组件,必须用 expose。


6️⃣ EventBus(小项目用,全局事件通信)

Vue3 没有直接提供,可以自己用 mitt:

import mitt from 'mitt'
export const bus = mitt()
bus.emit('login', user)
bus.on('login', handler)

适合:

  • 非父子组件通信
  • 简单的全局事件系统

7️⃣ Pinia(全局状态管理)

const store = useUserStore()
store.name = 'jack'

最适合:

  • 大型项目
  • 全局共享数据
  • 多组件同步更新

Vue3 官方推荐 Pinia 代替 Vuex。


🎯 三、面试官常问对比题(高频)


❓ 1:provide/inject 与 props 的区别?

面试最佳答案:

  • props 是父 → 子单层传递,适合常规通信
  • provide/inject 是跨层级传递,不用每层都写 props
  • provide 默认非响应式,需要 ref 才能响应式
  • props 是强约束(类型校验),inject 是弱约束(不保证存在)

❓ 2:v-model 原理是什么?

答:

Vue3 中 v-model 等价于 modelValue + update:modelValue
本质仍然是父传值、子触发更新事件。


❓ 3:expose 为什么需要?

因为:

  • Vue3 的 setup 隔离作用域,默认不会暴露内部实例方法
  • 使用 expose 可以决定哪些方法安全地开放给外部

❓ 4:为什么要用 store(Pinia)?不能用 eventBus 吗?

简答:

  • eventBus 是事件驱动,不适合作状态管理
  • Pinia 是响应式数据管理,有 devtools、有模块化、有持久化
  • 大项目必须用 store

❓ 5:provide / inject 为什么默认不是响应式?

因为当初设计它用于“静态配置”(比如主题),要避免不必要更新;

若需要响应式,需要传 ref 或 reactive:

provide('theme', reactive({ mode: 'dark' }))

🎯 四、一句话总结

Vue3 的通信体系基于 props/emit 构建,
provide/inject 解决跨层级传递,
expose 解决实例暴露,
store 解决全局共享,
eventBus 解决简单事件分发。

掌握这些,你能应对所有组件通信场景。