Vue3 父子组件通信
Vue3 父子组件通信(附示例)
在 Vue3 中,父子组件通信是构建复杂应用的核心技能之一。本教程将详细介绍 5 种常用通信方式,并提供完整代码示例,助你快速掌握组件间的数据与事件交互。
一、父传子:Props 传递数据
父组件通过 Props 向子组件传递数据,子组件通过 defineProps
接收。
示例代码:
<!-- Parent.vue -->
<template>
<Child :message="parentMsg" />
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const parentMsg = ref('Hello from Parent!');
</script>
<!-- Child.vue -->
<template>
<div>{{ message }}</div>
</template>
<script setup>
defineProps({
message: {
type: String,
required: true
}
});
</script>
关键点:
- 父组件通过
:message
绑定动态数据 - 子组件通过
defineProps
声明接收的 Props 类型
二、子传父:自定义事件(emit)
子组件通过 defineEmits
定义事件,父组件监听事件并处理数据。
示例代码:
<!-- Parent.vue -->
<template>
<Child @child-event="handleEvent" />
<p>收到子组件消息:{{ childMsg }}</p>
</template>
<script setup>
import { ref } from 'vue';
const childMsg = ref('');
const handleEvent = (msg) => {
childMsg.value = msg;
};
</script>
<!-- Child.vue -->
<template>
<button @click="sendMsg">发送消息</button>
</template>
<script setup>
const emit = defineEmits(['child-event']);
const sendMsg = () => {
emit('child-event', 'Hello from Child!');
};
</script>
关键点:
- 子组件通过
emit()
触发事件并传参 - 父组件通过
@child-event
监听事件
三、父访问子组件:ref + defineExpose
通过 ref
获取子组件实例,需配合 defineExpose
暴露方法/数据。
示例代码:
<!-- Parent.vue -->
<template>
<Child ref="childRef" />
<button @click="callChildMethod">调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue';
const childRef = ref(null);
const callChildMethod = () => {
childRef.value.showMessage(); // 调用子组件方法
console.log(childRef.value.count); // 访问子组件数据
};
</script>
<!-- Child.vue -->
<script setup>
import { ref, defineExpose } from 'vue';
const count = ref(0);
const showMessage = () => {
console.log('子组件方法被调用');
};
defineExpose({ showMessage, count }); // 暴露指定内容
</script>
关键点:
- 子组件需通过
defineExpose
显式暴露内容 - 父组件通过
ref.value
访问子组件实例
四、双向绑定:v-model 语法糖
通过 v-model
实现父子组件数据的双向绑定。
示例代码:
<!-- Parent.vue -->
<template>
<Child v-model="inputValue" />
</template>
<script setup>
import { ref } from 'vue';
const inputValue = ref('');
</script>
<!-- Child.vue -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
</template>
<script setup>
defineProps(['modelValue']);
defineEmits(['update:modelValue']);
</script>
等价形式:v-model
是 :modelValue + @update:modelValue
的语法糖
五、批量传递:$attrs 透传
通过 $attrs
传递未在 Props 中声明的属性,常用于高阶组件封装。
示例代码:
<!-- Parent.vue -->
<Child class="custom-class" data-id="123" />
<!-- Child.vue -->
<template>
<div v-bind="$attrs"> <!-- 透传所有未声明属性 -->
{{ $attrs.data-id }}
</div>
</template>
关键点:
$attrs
包含父组件传递的所有非 Props 属性- 通过
v-bind="$attrs"
实现属性透传
总结对比
方式 | 方向 | 适用场景 |
---|---|---|
Props | 父 → 子 | 传递初始数据、配置参数 |
Emit | 子 → 父 | 触发父组件逻辑、提交表单数据 |
ref + expose | 父 → 子 | 调用子组件方法/获取状态 |
v-model | 双向绑定 | 表单控件、自定义输入组件 |
$attrs | 父 → 子 | 高阶组件封装、属性透传 |
Vue3 父子组件通信补充:Slots(插槽)详解
在 Vue3 中,Slots(插槽) 是一种强大的父子组件通信方式,允许父组件向子组件传递模板片段,实现内容分发与动态布局。以下是 Slots 的三种核心用法及完整示例:
一、默认插槽(Default Slot)
作用:父组件向子组件传递通用内容模板,子组件通过 <slot>
标签接收。
适用场景:子组件作为容器,父组件自定义内部结构。
示例代码:
<!-- Parent.vue -->
<template>
<Child>
<p>这是父组件传递的默认内容</p>
</Child>
</template>
<!-- Child.vue -->
<template>
<div class="child-container">
<slot></slot> <!-- 默认插槽位置 -->
</div>
</template>
关键点:
父组件内容会替换子组件中的
<slot>
标签支持设置默认内容(当父组件未传内容时显示):
<slot>默认提示文字</slot>
二、具名插槽(Named Slots)
作用:父组件传递多个不同位置的模板,子组件通过 name
属性区分插槽位置。
示例代码:
<!-- Parent.vue -->
<template>
<Child>
<template v-slot:header>
<h2>标题区域</h2>
</template>
<template v-slot:content>
<p>正文内容</p>
</template>
</Child>
</template>
<!-- Child.vue -->
<template>
<div>
<slot name="header"></slot>
<slot name="content"></slot>
</div>
</template>
简写语法:
<template #header>...</template>
三、作用域插槽(Scoped Slots)
作用:子组件向父组件传递数据,父组件根据数据动态渲染内容。
示例代码:
<!-- Parent.vue -->
<template>
<Child>
<template #item="slotProps">
<span>{{ slotProps.text }} - {{ slotProps.index }}</span>
</template>
</Child>
</template>
<!-- Child.vue -->
<template>
<ul>
<li v-for="(item, index) in items" :key="index">
<slot name="item" :text="item" :index="index"></slot>
</li>
</ul>
</template>
<script setup>
const items = ['Apple', 'Banana', 'Orange'];
</script>
关键点:
- 子组件通过
<slot :data="value">
传递数据 - 父组件通过
v-slot:name="props"
接收数据 - 支持解构语法:
v-slot:item="{ text, index }"
四、动态插槽名(Dynamic Slots)
作用:通过动态指令参数实现插槽名的动态绑定。
示例代码:
<template>
<Child>
<template #[dynamicSlotName]>
<!-- 动态内容 -->
</template>
</Child>
</template>
<script setup>
const dynamicSlotName = ref('header');
</script>
对比总结
类型 | 数据流向 | 核心能力 |
---|---|---|
默认插槽 | 父 → 子 | 基础内容分发 |
具名插槽 | 父 → 子 | 多区域内容分发 |
作用域插槽 | 子 → 父 | 子组件数据反哺父组件渲染逻辑 |
动态插槽 | 双向 | 动态控制插槽位置 |
最佳实践
- 组件解耦:通过插槽实现 UI 与逻辑分离,提升组件复用性
- 组合式 API:在
<script setup>
中可直接使用插槽逻辑 - 性能优化:避免在插槽内容中使用复杂计算,必要时用
v-memo
缓存
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 万家灯火