组件间的关系可以分为:
- 父子关系。以前文的代码为例,最典型的就是App.vue与Car.vue这种,APP是父,Car是子。
- 祖孙关系。如果Car再引入一个子组件,这个子组件与App就是祖孙关系。
- 其他。比如Car和Staff。
基于以上的几种关系,组件间的通信方式有好些个实现。后面会讲解一些在开发中常见的方法。不多说,直接上代码。Father.vue:
<template>
<div class="father">
<h1>父组件</h1>
运单ID:{{ order.orderId }},
货主:{{ order.shipperName }},
发车时间:{{ order.departTime }}
<button @click="changeDriver">修改子组件的Driver</button>
<h2></h2>
<Son ref="sonRef" :order="order" :setDepartTime="getDepartTime" />
</div>
</template>
<script lang='ts' setup name='Father'>
import Son from './Son.vue';
import { reactive, ref } from 'vue';
import { type Order } from '@/params';
const order = reactive<Order>({
orderId: "111",
shipperId: "222",
shipperName: "货主A",
departTime: "待定"
})
function getDepartTime(val: string) {
order.departTime = val
}
const sonRef = ref()
function changeDriver() {
sonRef.value.driver.driverName = "修改后的司机C"
}
</script>
<style scoped>
.father {
background-color: darkseagreen;
}
</style>
Son.vue:
<template>
<div class="son">
<h1>子组件</h1>
司机:{{ driver.driverName }}<br />
发车时间:{{ order.departTime }}
<button @click="departTime">发车上报</button>
来自父组件的Order:
运单ID:{{ order.orderId }},
货主:{{ order.shipperName }}
</div>
</template>
<script lang='ts' setup name='Son'>
import { reactive,watch } from 'vue';
import { type Order } from '@/params';
// 定义了子组件要接收的属性和方法,以及对应的类型。?代表可选
interface Props {
order?: Order,
setDepartTime: Function
}
//响应式Props解构,并指定父组件不传时的默认值
const {
order = { orderId: "0", shipperId: "0", shipperName: "无" },
setDepartTime
} = defineProps<Props>()
//调用父组件的方法
function departTime() {
setDepartTime("2025-01-01 11:11:11."+Math.random())
}
//必须用deep:true才能侦听到变化
watch(() => order, (newVal) => {
console.log('Order变了:', newVal);
},{deep:true})
const driver = reactive({ driverId: "AAAA", driverName: "司机B" })
// 暴露给父组件
defineExpose({ driver })
</script>
<style scoped>
.son {
background-color: silver;
}
</style>
props的写法
- interface如何定义看前文。
- 本文写法适用Vue3.5+。
- 父传子:父组件定义了响应式数据Order,以及函数getDepartTime(修改发车时间),在引入的子组件上通过<Son :order="order" :getDepartTime="getDepartTime"/>
- 传给子组件。:order代表参数的key,="order"为定义的响应式数据(写的一样,但是要注意性质不同)。
- 子传父:就是父组件提供了一个函数给子组件调用,注意“setDepartTime”和“getDepartTime”的写法。真正的子传父应该用$emit,这里仅展示一下可能性。
ref & defineExpose的写法
- 子组件定义了响应式数据driver,通过defineExpose({ driver })暴露出去。
- 除了数据,也可以暴露函数出去。
- 父组件通过ref创建对子组件实例的引用<Son ref="sonRef"/>,然后再通过其实例(sonRef)访问子组件暴露的数据或方法。
- 其实此方式不能称之为“子组件传数据或方法给父组件”,应该是“父组件使用子组件暴露的数据或方法”。真正子传父,应该使用事件$emit,后文再叙。
- defineExpose除了暴露子组件的内部方法或属性给父组件调用、调试测试外,还用于将可复用的组件内的方法暴露给外部调用;还能集成第三方库,暴露必要的实例或方法。