Vue 3 父子&爷孙组件通信

1. 父组件给子组件传值 (Props):子用父值

基本用法

父组件 (ParentComponent.vue):

vue 复制代码
<template>
  <ChildComponent :title="childTitle" :count="childCount" />
</template>

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

const childTitle = ref('子组件标题')
const childCount = ref(10)
</script>

子组件 (ChildComponent.vue):

vue 复制代码
<template>
  <div>
    <h2>{{ title }}</h2>
    <p>Count: {{ count }}</p>
  </div>
</template>

<script setup>
// 定义 props
const props = defineProps({
  title: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0
  }
})
</script>

文档参考

2. 父组件调用子组件的方法 (Expose & Ref):父用子方法

父组件 (ParentComponent.vue):

vue 复制代码
<template>
  <ChildComponent ref="childRef" />
  <button @click="callChildMethod">调用子组件方法</button>
</template>

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

const childRef = ref(null)

function callChildMethod() {
  if (childRef.value) {
    childRef.value.increment() //直接调用子组件中的increment的方法
  }
}
</script>

子组件 (ChildComponent.vue):

vue 复制代码
<template>
  <div>子组件计数: {{ count }}</div>
</template>

<script setup>
import { ref } from 'vue'

const count = ref(0)

// 暴露给父组件的方法
function increment() {
  count.value++
}

// 如果子组件中用const XX 来定义方法,就需要 使用 defineExpose 暴露方法,父组件才能用到
defineExpose({
  increment
})
</script>

文档参考

3. 子组件改变父组件中的值 (Emit Events):子改父值

父组件 (ParentComponent.vue):

vue 复制代码
<template>
  <div>父组件计数: {{ parentCount }}</div>
  <ChildComponent @update-count="updateCount" />
</template>

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

const parentCount = ref(0)

function updateCount(newValue) {
  parentCount.value = newValue
}
</script>

子组件 (ChildComponent.vue):

vue 复制代码
<template>
  <button @click="handleClick">更新父组件计数</button>
</template>

<script setup>
// 定义 emits
const emit = defineEmits(['updateCount'])

function handleClick() {
  // 触发事件并传递新值
  emit('updateCount', 5)
}
</script>

使用 v-model 实现双向绑定:父传子&子传父

父组件 (ParentComponent.vue):

vue 复制代码
<template>
  <ChildComponent v-model="count" />
</template>

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

const count = ref(0)
</script>

子组件 (ChildComponent.vue):

vue 复制代码
<template>
  <button @click="updateValue">增加: {{ modelValue }}</button>
</template>

<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

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

文档参考

4. 子组件调用父组件的方法 (Emit Events): 子用父方法

父组件 (ParentComponent.vue):

vue 复制代码
<template>
  <ChildComponent @call-parent="parentMethod" />
</template>

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

function parentMethod(message) {
  console.log('父组件方法被调用:', message)
}
</script>

子组件 (ChildComponent.vue):

vue 复制代码
<template>
  <button @click="callParent">调用父组件方法</button>
</template>

<script setup>
const emit = defineEmits(['callParent'])

function callParent() {
  emit('callParent', '来自子组件的消息')
}
</script>

5. 其他通信方式

依赖注入 (Provide/Inject)

适合跨多层组件通信

祖先组件 (AncestorComponent.vue):

vue 复制代码
<template>
  <ParentComponent />
</template>

<script setup>
import { provide, ref } from 'vue'
import ParentComponent from './ParentComponent.vue'

const sharedData = ref('共享数据')

provide('sharedKey', sharedData)
</script>

后代组件 (DescendantComponent.vue):

vue 复制代码
<script setup>
import { inject } from 'vue'

const sharedData = inject('sharedKey')
</script>

文档参考

总结对比表

通信方式 方向 适用场景 优点 缺点
Props 父 → 子 传递初始数据 明确的数据流,易于理解 单向数据流
Emit Events 子 → 父 子组件通知父组件 明确的事件流 需要父组件监听
Refs & Expose 父 → 子 访问子组件方法/属性 直接访问 破坏封装性
v-model 双向 表单输入等双向绑定 语法糖,简洁 仅适用于特定场景
Provide/Inject 任意 跨多层组件 避免逐层传递 数据来源不明确

最佳实践建议

  1. 优先使用 props 和 emits:保持数据流的清晰和可预测性
  2. 谨慎使用 refs:这会破坏组件的封装性
  3. 复杂场景使用状态管理:如 Pinia 适用于全局或复杂状态
  4. 考虑组件设计:如果通信过于复杂,可能需要重新思考组件结构

完整文档参考

相关推荐
对不起初见42 分钟前
PlantUML 完整教程:从入门到精通
前端·后端
东方掌管牛马的神1 小时前
oh-my-zsh 配置与使用技巧
前端
你的人类朋友1 小时前
HTTP请求结合HMAC增加安全性
前端·后端·安全
aidingni8881 小时前
掌握 TCJS 游戏摄像系统:打造动态影院级体验
前端·javascript
有梦想的攻城狮1 小时前
从0开始学vue:npm命令详解
前端·vue.js·npm
我是日安2 小时前
从零到一打造 Vue3 响应式系统 Day 23 - Watch:基础实现
前端·javascript·vue.js
FogLetter2 小时前
TypeScript 泛型:让类型也拥有“函数式”超能力
前端·typescript
FogLetter2 小时前
Map 与 WeakMap:内存管理的艺术与哲学
前端·javascript
golang学习记2 小时前
从0死磕全栈之Next.js 流式渲染(Streaming)实战:实现渐进式加载页面,提升用户体验
前端
前端伪大叔2 小时前
第15篇:Freqtrade策略不跑、跑错、跑飞?那可能是这几个参数没配好
前端·javascript·后端