父子组件通信详解

我们将系统讲解以下几种通信方式:

场景 通信方向 常用机制
父 → 子 父组件传数据给子组件 props
子 → 父 子组件传事件/数据给父组件 emit
父 ↔ 子 父子互相修改数据 v-model(语法糖)
深层通信 任意组件层级间传值 provide / inject
兄弟组件 无父子关系的通信 事件总线 / 状态管理(Pinia / Vuex)

一、父传子 --- props

✅ 基础用法

父组件:

html 复制代码
<template>
  <UserCard :name="userName" :age="userAge" />
</template>

<script setup>
import UserCard from './UserCard.vue'

const userName = 'Alice'
const userAge = 25
</script>

子组件:

html 复制代码
<script setup>
const props = defineProps({
  name: String,
  age: Number
})
</script>

<template>
  <div>{{ name }} --- {{ age }}岁</div>
</template>

🧠 解释:

  • defineProps() 是 Vue 3 <script setup> 提供的编译宏,用于声明接收的属性。

  • 传递数据时建议加 :(动态绑定),否则会被当作字符串。


⚙️ 默认值 & 类型验证

html 复制代码
<script setup>
const props = defineProps({
  name: {
    type: String,
    default: '未命名'
  },
  age: {
    type: Number,
    required: true
  }
})
</script>
  • default:提供默认值

  • required:必传属性

  • type:类型限制(调试模式下会警告)


⚠️ 单向数据流原则

子组件不应直接修改 props!

错误示例:

javascript 复制代码
props.age++ // ❌

正确做法是复制:

javascript 复制代码
const localAge = ref(props.age)

或者通过事件让父组件更新(见下一节)。


二、子传父 --- emit

✅ 基本示例

子组件:

html 复制代码
<script setup>
const emit = defineEmits(['update'])

function handleClick() {
  emit('update', '来自子组件的新数据')
}
</script>

<template>
  <button @click="handleClick">通知父组件</button>
</template>

父组件:

html 复制代码
<template>
  <Child @update="onUpdate" />
</template>

<script setup>
import Child from './Child.vue'

function onUpdate(data) {
  console.log('收到子组件数据:', data)
}
</script>

🧠 解释:

  • 子组件定义 defineEmits(['update'])

  • 使用 emit('update', payload) 触发事件

  • 父组件用 @update 监听


⚙️ 事件参数校验(Vue 3.3+ 新特性)

javascript 复制代码
const emit = defineEmits({
  update(payload) {
    if (typeof payload !== 'string') {
      console.warn('参数必须是字符串')
      return false
    }
    return true
  }
})

三、父子双向通信 --- v-model

Vue 3 中 v-modelprops + emit 的语法糖。

✅ 基础示例

父组件:

html 复制代码
<template>
  <Child v-model="message" />
  <p>父组件数据: {{ message }}</p>
</template>

<script setup>
import Child from './Child.vue'
import { ref } from 'vue'

const message = ref('Hello')
</script>

子组件:

html 复制代码
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

function change() {
  emit('update:modelValue', props.modelValue + ' Vue3')
}
</script>

<template>
  <button @click="change">修改父组件数据</button>
</template>

🧠 底层机制:

父组件写法 子组件内部等价
v-model="data" :modelValue="data" @update:modelValue="data = $event"

⚙️ 多个 v-model

支持多个绑定字段:

父组件:

html 复制代码
<Child v-model:title="title" v-model:content="content" />

子组件:

html 复制代码
<script setup>
const props = defineProps(['title', 'content'])
const emit = defineEmits(['update:title', 'update:content'])
</script>

四、跨层级通信 --- provide / inject

当父组件与子孙组件之间层级较深时,可以使用 依赖注入机制

祖先组件:

html 复制代码
<script setup>
import { provide, ref } from 'vue'

const user = ref('Tom')
provide('user', user)
</script>

<template>
  <ChildA />
</template>

任意后代组件:

html 复制代码
<script setup>
import { inject } from 'vue'
const user = inject('user')
</script>

<template>
  <p>用户: {{ user }}</p>
</template>

🧠 特点:

  • 非响应式变量建议用 ref 包装;

  • 后代组件可以修改注入的值;

  • 常用于全局配置、主题共享、依赖注入(如表单上下文)。


五、兄弟组件通信方案

Vue 没有直接的兄弟通信,需要借助:

1️⃣ 事件总线(Event Bus)

手动创建一个全局事件实例(简单项目可用):

html 复制代码
// eventBus.js
import mitt from 'mitt'
export default mitt()

使用:

html 复制代码
// 组件A
import bus from './eventBus'
bus.emit('change', 123)

// 组件B
import bus from './eventBus'
bus.on('change', val => console.log(val))

2️⃣ 全局状态管理(Pinia / Vuex)

中大型项目推荐使用 Pinia 管理全局状态:

html 复制代码
npm install pinia
html 复制代码
// store.js
import { defineStore } from 'pinia'
export const useMainStore = defineStore('main', {
  state: () => ({ count: 0 }),
})

六、总结对比表

方式 方向 优点 缺点 场景
props 父 → 子 简单直观 单向,不能反向修改 普通父传子
emit 子 → 父 清晰、解耦 只能往上一级传 按钮点击、表单回传
v-model 父 ↔ 子 双向绑定,简洁 内部实现较复杂 表单组件
provide/inject 任意层级 深层传值方便 调试不便 全局配置/依赖注入
事件总线 / Pinia 任意组件 解耦、灵活 管理复杂 大型项目、兄弟通信
相关推荐
Wenweno0o14 小时前
0基础Go语言Eino框架智能体实战-chatModel
开发语言·后端·golang
于慨14 小时前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
石小石Orz14 小时前
油猴脚本实现生产环境加载本地qiankun子应用
前端·架构
从前慢丶14 小时前
前端交互规范(Web 端)
前端
@yanyu66614 小时前
07-引入element布局及spring boot完善后端
javascript·vue.js·spring boot
CHU72903515 小时前
便捷约玩,沉浸推理:线上剧本杀APP功能版块设计详解
前端·小程序
chenjingming66615 小时前
jmeter线程组设置以及串行和并行设置
java·开发语言·jmeter
GISer_Jing15 小时前
Page-agent MCP结构
前端·人工智能
王霸天15 小时前
💥别再抄网上的Scale缩放代码了!50行源码教你写一个永不翻车的大屏适配
前端·vue.js·数据可视化
小领航15 小时前
用 Three.js + Vue 3 打造炫酷的 3D 行政地图可视化组件
前端·github