前言
在 Vue 3 开发中,父子组件通信是最基础、最常用的功能之一。
很多新手可能只知道 props
,但实际上 Vue 3 提供了多种方式,适用于不同场景。
本文会详细讲解 5 种父传子通信方式,并附上完整代码示例,帮你彻底掌握!
1. Props(最基础的方式)
1.1 基本用法
父组件通过 props
向子组件传递数据,子组件通过 defineProps
接收。
<!-- 父组件 Parent.vue -->
<template>
<Child :title="msg" :count="num" />
</template>
<script setup>
import Child from './Child.vue';
const msg = "Vue 3 Props";
const num = 100;
</script>
<!-- 子组件 Child.vue -->
<template>
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
</template>
<script setup>
const props = defineProps({
title: String, // 类型校验
count: Number,
});
</script>
1.2 Props 高级用法
(1) 默认值 & 必填
defineProps({
title: {
type: String,
required: true, // 必须传
},
count: {
type: Number,
default: 0, // 默认值
},
});
(2) 复杂类型校验
defineProps({
user: {
type: Object,
default: () => ({ name: 'Guest' }), // 对象/数组必须用函数返回
},
list: {
type: Array,
validator: (arr) => arr.length > 0, // 自定义校验
},
});
(3) TypeScript 支持
<script setup lang="ts">
interface Props {
title: string;
count?: number; // 可选
}
const props = defineProps<Props>();
</script>
-
interface Props
:这是 TypeScript 的语法,用来定义 props 的类型。 -
title: string
:title
必须是字符串。 -
count?: number
:?
表示count
是可选的(可以不传)。 -
defineProps<Props>()
:用 TypeScript 的方式定义 props。
2. v-model(父子双向绑定)
2.1 基本用法
<!-- 父组件 Parent.vue -->
<template>
<Child v-model="inputValue" />
<p>父组件值: {{ inputValue }}</p>
</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>
2.2 多个 v-model
<!-- 父组件 Parent.vue -->
<Child v-model:first="firstName" v-model:last="lastName" />
<!-- 子组件 Child.vue -->
<template>
<input :value="first" @input="$emit('update:first', $event.target.value)" />
<input :value="last" @input="$emit('update:last', $event.target.value)" />
</template>
<script setup>
defineProps(['first', 'last']);
defineEmits(['update:first', 'update:last']);
</script>
3. $refs(父调子方法)
3.1 基本用法
<!-- 父组件 Parent.vue -->
<template>
<Child ref="childRef" />
<button @click="childRef.sayHello()">调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue';
const childRef = ref();
</script>
<!-- 子组件 Child.vue -->
<script setup>
function sayHello() {
alert('Hello from Child!');
}
// 暴露方法给父组件
defineExpose({ sayHello });
</script>
4. Provide / Inject(跨层级通信)
4.1 基本用法
<!-- 父组件 Parent.vue -->
<script setup>
import { provide } from 'vue';
provide('theme', 'dark'); // 提供数据
</script>
<!-- 子组件 Child.vue -->
<script setup>
import { inject } from 'vue';
const theme = inject('theme', 'light'); // 第二个参数是默认值
</script>
4.2 响应式数据
<!-- 父组件 Parent.vue -->
<script setup>
import { provide, ref } from 'vue';
const count = ref(0);
provide('count', count);
</script>
<!-- 子组件 Child.vue -->
<script setup>
import { inject } from 'vue';
const count = inject('count');
</script>
5. Event Bus(全局事件总线)
适用于任意组件通信(Vue 3 推荐使用 mitt
库)。
5.1 安装 mitt
npm install mitt
5.2 使用示例
// eventBus.js
import mitt from 'mitt';
export const eventBus = mitt();
<!-- 父组件 Parent.vue -->
<script setup>
import { eventBus } from './eventBus';
eventBus.emit('update-data', { newData: 123 });
</script>
<!-- 子组件 Child.vue -->
<script setup>
import { eventBus } from './eventBus';
eventBus.on('update-data', (data) => {
console.log(data); // { newData: 123 }
});
</script>
总结
方式 | 适用场景 | 特点 |
props | 父传子数据 | 支持数据类型验证和默认值 |
v-model | 父子双向绑定 | 适用于表单 |
$refs | 父调子方法 | 直接操作子组件 |
provide/inject | 跨层级传递数据 | 避免props一层层传递 |
Event Bus | 任意组件通信 | 全局事件监听 |