第 12 题:Vue(尤其 Vue3)中父子组件通信方式有哪些?区别是什么?
这是 Vue 面试必考题,至少 90% 面试官会问。
🎯 一、标准答案(你在面试里应该先说的)
Vue3 中父子组件通信有 七 大常用方式:
父 → 子
- Props(最常见)
- Expose(父通过 ref 调用子方法)
子 → 父
- Emit 事件
父 ↔ 子
- v-model(语法糖,本质是 props + emit)
跨层级
- provide / inject
更大范围
- Pinia / Vuex(状态管理)
更底层/特殊情况
- EventBus(Vue3 不推荐,但面试会问)
🎯 二、父 → 子通信方式
1️⃣ Props(最常用)
父组件:
ini
<Child :msg="text" />
子组件:
scss
defineProps({
msg: String
})
Props 特性:
- 单向数据流(父 → 子)
- 子不允许修改 props(会直接报错)
- 适合传递配置类、静态数据
2️⃣ defineExpose(父组件通过 ref 调用子组件方法)
子组件:
scss
const submit = () => { ... }
defineExpose({ submit })
父组件:
ini
<Child ref="childRef" />
scss
childRef.value.submit()
使用场景:
- 子组件希望暴露一些内部方法给父组件
- 提交表单、重置、校验等
🎯 三、子 → 父通信方式
3️⃣ Emit 事件
子组件:
scss
const emit = defineEmits(['update'])
emit('update', newValue)
父组件:
ini
<Child @update="handleUpdate" />
特点:
- 单向数据流(子 → 父)
- 组件解耦,官方推荐方式
🎯 四、父 ↔ 子 双向通信
4️⃣ v-model(语法糖)
父组件:
ini
<Child v-model="value" />
子组件:
scss
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
emit('update:modelValue', 123)
本质只是 props + emit 的语法糖。
🎯 五、跨层级通信
5️⃣ provide / inject
祖先组件:
arduino
provide('theme', 'dark')
任意层子组件:
ini
const theme = inject('theme')
特点:
- 多层级,不需要一层一层传 props
- 常用于配置类、上下文类数据(如表单组件库)
🎯 六、全局状态通信
6️⃣ Pinia(或 Vuex)
适合全局数据:
- 用户信息
- 权限状态
- 全局设置
- 缓存数据
Pinia 是 Vue3 官方主推状态库。
🎯 七、特殊情况
7️⃣ EventBus(Vue3 不推荐,但要知道)
适合简单的事件广播场景:
javascript
import mitt from 'mitt'
export const bus = mitt()
组件 A:
arduino
bus.emit('hello', 123)
组件 B:
dart
bus.on('hello', val => {})
🔥 六、面试官常见追问(附高能回答)
追问 1:父子通信和 provide/inject 的区别是什么?什么时候用哪个?
你应该这样答:
- 父子 props/emit:适合直接父子通信,数据清晰可控
- provide/inject:适合跨层级通信,比如 3、5 层以上
- provide/inject 数据不是响应式(除非你传 ref 或 reactive)
✔ 高分补充:
provide/inject 更像"依赖注入",在 UI 组件库中用得非常多(Element Plus、Ant Design Vue)。
追问 2:v-model 为什么能实现双向绑定?底层是什么?
标准回答:
v-model 本质是 props(modelValue) + emit(update:modelValue)
就是:
父给子:modelValue
子返父:update:modelValue
追问 3:defineExpose 是什么时候用?和 props/emit 的关系?
答:
-
expose 用来暴露方法给父组件调用
-
和 props/emit 不一样:
- props/emit 用于 数据
- expose 用于 方法、行为
例如表单组件:
scss
defineExpose({
validate,
reset
})
追问 4:provide 是不是响应式的?如何让它变成响应式?
默认 不是响应式(非常多人不知道!)
必须这样传:
csharp
const theme = ref('dark')
provide('theme', theme)
inject 处能响应变化。
追问 5:EventBus 为什么不推荐?
- 容易造成事件混乱
- 不易追踪
- 大项目可维护性差
- TypeScript 支持差
面试官会觉得你非常专业。
🎯 七、一句话总结(背下来就能面试用)
Vue3 父子通信分:props、emit、v-model、expose,跨层级用 provide/inject,全局用 Pinia,少量事件可用 EventBus。