在 Vue 3 中,父子组件之间的通信是构建交互式应用的基础。主要有两种情况:父组件向子组件传递数据(父传子),以及子组件向父组件发送消息(子传父)。以下是这两种情况的详细说明和示例:
父传子(Props 下传)
步骤:
-
在父组件中定义数据: 父组件中定义要传递给子组件的数据。
-
通过 Props 传递数据: 使用
props
将数据从父组件传递到子组件。 -
在子组件中声明 Props: 子组件通过
defineProps
函数声明它期望从父组件接收的props
。
示例:
父组件
<template>
<div>
<ChildComponent :parentData="data" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const data = ref('这是来自父组件的数据');
</script>
子组件
<template>
<div>
<p>{{ parentData }}</p>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
parentData: String
});
</script>
子传父(Events 上传)
步骤:
-
在子组件中触发事件: 子组件使用
defineEmits
函数声明它可以触发的事件,并使用emit
方法触发事件。 -
在父组件中监听事件: 父组件通过
v-on
或@
指令监听子组件触发的事件,并定义处理函数。
示例:
子组件
<template>
<button @click="sendDataToParent">发送数据到父组件</button>
</template>
<script setup>
import { defineEmits } from 'vue';
const emit = defineEmits(['sendData']);
function sendDataToParent() {
emit('sendData', '这是来自子组件的数据');
}
</script>
父组件
<template>
<div>
<ChildComponent @sendData="handleData" />
</div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
function handleData(data) {
console.log(data); // 输出:这是来自子组件的数据
}
</script>
另一种方法( defineExpose
)
子组件中使用 defineExpose
<script setup>
import { ref, defineExpose } from 'vue';
const count = ref(0);
function increment() {
count.value++;
}
// 通过 defineExpose 暴露给父组件或模板引用
defineExpose({
count,
increment
});
</script>
父组件中访问子组件暴露的内容
<template>
<!-- 给子组件设置一个 ref 名称 -->
<ChildComponent ref="childRef" />
<button @click="callChildMethod">调用子组件方法</button>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import ChildComponent from './ChildComponent.vue';
const childRef = ref(null);
// 使用 onMounted 确保子组件已经挂载
onMounted(() => {
if (childRef.value) {
console.log('子组件 count:', childRef.value.count); // 访问暴露的 count
}
});
function callChildMethod() {
if (childRef.value) {
childRef.value.increment(); // 调用暴露的 increment 方法
}
}
</script>
- 显式暴露:
defineExpose
允许你显式地选择哪些属性或方法需要暴露给父组件或模板引用,而不是自动暴露所有。 - 更好的封装:通过
defineExpose
,你可以更好地封装组件的内部状态和逻辑,只暴露必要的接口。 - 类型安全:在使用 TypeScript 时,
defineExpose
可以帮助你确保类型正确性,并且可以在开发工具中提供更好的智能提示。
注意事项
- 生命周期:确保在访问子组件暴露的属性或方法时,子组件已经挂载并且相应的数据或方法已经初始化。
- 响应性:暴露的响应式状态(如
ref
或reactive
对象)在父组件中是响应式的,可以直接观察和使用。