基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

Vue父子组件通信有哪些实现方式?

知识点图片

Vue 中父子组件通信是开发中最基础也是最高频的场景。根据 Vue 2 和 Vue 3 的不同特性,以及通信的方向(父传子、子传父、双向),主要有以下 8 种 常见的实现方式:


1. Props(父传子)—— 最通用

这是最基础的方式。父组件通过属性绑定传递数据,子组件通过 props 接收。

  • 适用场景: 父组件向子组件传递静态或动态数据。
  • 特点: 单向数据流(父组件数据更新会流向子组件,反之不行)。
html
<!-- 父组件 -->
<Child :msg="message" />

<!-- 子组件 -->
<script>
props: {
  msg: String
}
</script>

2. $emit / v-on(子传父)—— 最通用

子组件通过触发事件通知父组件,父组件监听该事件并接收数据。

  • 适用场景: 子组件需要修改父组件数据,或通知父组件执行操作。
js
// 子组件
this.$emit('update-data', newValue);

// 父组件
<Child @update-data="handleUpdate" />

3. v-model(双向绑定)

v-model 本质上是 props + event 的语法糖。

  • Vue 2: 默认为 value prop 和 input 事件。
  • Vue 3: 默认为 modelValue prop 和 update:modelValue 事件。支持多个 v-model。
html
<!-- 父组件 -->
<Child v-model="count" />

<!-- 子组件 (Vue 3 写法) -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
  <button @click="$emit('update:modelValue', modelValue + 1)">+1</button>
</template>

4. Refs & defineExpose(父访问子)

父组件通过 ref 获取子组件的实例,直接调用子组件的方法或访问数据。

  • 注意:
    • Vue 2: 直接访问 this.$refs.childName
    • Vue 3 (<script setup>): 组件默认是关闭的(私有的),必须在子组件中通过 defineExpose 暴露属性或方法,父组件才能访问。
js
// 子组件 (Vue 3)
const openModal = () => { ... }
defineExpose({ openModal }) // 显式暴露

// 父组件
const childRef = ref(null)
childRef.value.openModal()

5. Provide / Inject(依赖注入)

用于祖孙组件层级很深的父子组件通信,避免“Props Drilling”(属性透传)。

  • 适用场景: 通用的配置、主题、用户信息的传递。
  • 特点: 父组件(Provider)提供数据,后代组件(Injector)无论多深都可以注入使用。若传递的是响应式对象(如 refreactive),则后代也能响应更新。
js
// 父组件 (Ancestor)
provide('theme', 'dark')

// 后代组件 (Descendant)
const theme = inject('theme')

6. $attrs(透传 Attributes)

当父组件传递的属性没有被子组件声明为 propsemits 时,这些属性会包含在 $attrs 中。

  • 适用场景: 封装高阶组件(HOC)或基础 UI 组件时,将父组件传递的 classstyle 或原生事件直接透传给子组件内部的根元素或特定元素。
  • Vue 3 变化: $listeners 已被移除,事件监听器现在也包含在 $attrs 中。
html
<!-- 父组件 -->
<BaseInput placeholder="请输入" @change="handleChange" />

<!-- 子组件 BaseInput -->
<template>
  <!-- v-bind="$attrs" 将 placeholder 和 change 事件都绑定到了 input 上 -->
  <input v-bind="$attrs" />
</template>

7. 作用域插槽 (Scoped Slots)

虽然主要用于内容分发,但它允许子组件将数据传递回父组件的插槽内容中

  • 适用场景: 父组件定义样式/布局,但数据内容由子组件提供(例如列表渲染组件)。
html
<!-- 子组件 -->
<slot :item="itemData"></slot>

<!-- 父组件 -->
<Child>
  <template v-slot="{ item }">
    <span>{{ item.name }}</span>
  </template>
</Child>

8. parent/parent /children (不推荐)

  • $parent: 子组件访问父组件实例。
  • $children: 父组件访问子组件实例数组。
  • 现状:
    • Vue 2 中可用。
    • Vue 3 中 $children 已被移除
    • 弊端: 导致父子组件强耦合,难以维护,通常不建议在业务代码中使用。

总结与最佳实践

方式 方向 推荐指数 适用场景
Props / Emit 父 ↔ 子 ⭐⭐⭐⭐⭐ 最基础、最常用的业务逻辑通信。
v-model 双向 ⭐⭐⭐⭐⭐ 表单组件、状态同步组件。
Provide / Inject 跨层级 ⭐⭐⭐⭐ 插件开发、深层嵌套组件共享数据。
Refs 父 → 子 ⭐⭐⭐ 需要手动触发子组件方法(如聚焦、重置、打开弹窗)。
Slots 子 → 父(UI) ⭐⭐⭐⭐ 布局复用,逻辑与视图分离。
Pinia / Vuex 全局 ⭐⭐⭐⭐⭐ 兄弟组件通信、跨页面复杂状态管理。
EventBus (mitt) 任意 ⭐⭐ 简单项目中的非父子通信(Vue 3 已移除原生 on/on/off,需引入库)。

面试回答策略:
建议先说最常用的 Props$emit,然后补充 v-modelRefs,最后提到跨层级的 Provide/Inject 和全局状态管理 Pinia,这样能体现你对 Vue 生态的全面掌握。

00:00
00:00