1. 使用 defineModel 来实现双向数据绑定
<template>
<div style="border: 2px solid black">
<h2>Child.vue</h2>
<div>{{ modelValue }}</div>
<button @click="handleChange">修改</button>
</div>
</template>
<script setup lang="ts">
import type { IUser } from "./Parent.vue";
const [modelValue, modelModifiers] = defineModel<IUser>({
type: Object,
default: {
name: "--",
age: "--",
},
});
const handleChange = () => {
modelValue.value.name = "李四";
console.log("==> modelModifiers:", modelModifiers);
};
</script>
<style lang="scss" scoped></style>
<template>
<div style="border: 2px solid black; box-sizing: border-box; padding: 30px">
<h2>Parent.vue</h2>
<div>{{ user }}</div>
<Child v-model.lazy="user" />
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Child from "./Child.vue";
export interface IUser {
name: string;
age: number;
}
const user = ref<IUser>({
name: "张三",
age: 18,
});
</script>
<style lang="scss" scoped></style>
<template>
<Parent />
</template>
<script setup lang="ts">
import Parent from "./Parent.vue";
</script>
<style lang="scss" scoped></style>
2. 使用 defineExpose + Proxy 获取子组件的 ref
<template>
<div class="myInput">
<component :is="h(ElInput, $attrs, $slots)" ref="inputRef" />
</div>
</template>
<script setup lang="ts">
import { h, ref } from "vue";
import { ElInput } from "element-plus";
const inputRef = ref();
defineExpose(
new Proxy(
{},
{
get(_target, prop) {
return inputRef.value?.[prop];
},
has(_target, prop) {
return prop in inputRef.value;
},
}
)
);
</script>
<style lang="scss" scoped></style>
<template>
<div class="">
<p>
<MyInputPlus ref="inputPlusRef" />
</p>
<button @click="handleSetInputFocus">set input focus</button>
<button @click="handleSetInputBlur">set input blur</button>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import MyInputPlus from "./MyInputPlus.vue";
const inputPlusRef = ref();
const handleSetInputFocus = () => {
inputPlusRef.value.focus?.();
};
const handleSetInputBlur = () => {
inputPlusRef.value.blur?.();
};
</script>
<style lang="scss" scoped></style>
3. 使用 h 函数透传 $slots
<template>
<div class="myInput">
<div class="prefix">
<slot name="prefix"> left </slot>
</div>
<input type="text" placeholder="TestTransferSlots" />
<div class="suffix">
<slot name="suffix"> right </slot>
</div>
</div>
</template>
<script setup lang="ts"></script>
<style lang="scss" scoped>
.myInput {
display: flex;
gap: 5px;
}
</style>
<template>
<div class="">
<component :is="h(MyInput, $attrs, $slots)" />
</div>
</template>
<script setup lang="ts">
import { h, ref } from "vue";
import MyInput from "./MyInput.vue";
</script>
<style lang="scss" scoped></style>
<template>
<div>
<MyInputPlus>
<template #prefix> + </template>
<template #suffix> - </template>
</MyInputPlus>
</div>
</template>
<script setup lang="ts">
import MyInputPlus from "./MyInputPlus.vue";
</script>
<style lang="scss" scoped></style>
4. 使用 @vue:xxx 监听子组件生命周期
<template>
<div class="">Child</div>
</template>
<script setup lang="ts">
import { onMounted } from "vue";
onMounted(async () => {
console.log("==> Child onMounted");
});
</script>
<style lang="scss" scoped></style>
<template>
<div class="">
<Child @vue:mounted="handleChildMounted" />
</div>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
const handleChildMounted = () => {
console.log("==> [Parent]:Child mounted");
};
</script>
<style lang="scss" scoped></style>
<template>
<div class="">
<Parent />
</div>
</template>
<script setup lang="ts">
import Parent from "./Parent.vue";
</script>
<style lang="scss" scoped></style>