组件交互
组件交互
Vue3 提供了更加灵活和强大的组件交互方式。如组件事件、组件传递 props、组件插槽、依赖注入、异步组件的代码
1. 组件传递 props
一个组件可以通过 props 接收另一个组件传递的数据。
子组件定义参数
子组件 src\components\ChildProps.vue
接收来自父组件的属性 msg1
, msg2
, msg3
<script setup>
import { defineProps } from "vue";
// defineProps(["msg1", "msg2", "msg3"]);
defineProps({
msg1: { type: String },
msg2: { type: String, default: "Default2" },
msg3: { type: String, default: "Default3", required: true },
});
</script>
<template>
<div>
<p>msg1: {{ msg1 }}</p>
<p>msg2: {{ msg2 }}</p>
<p>msg3: {{ msg3 }}</p>
</div>
</template>
注意事项:
- 在子组件定义通过
defineProps(params)
来定义属性,在父组件传递对应属性 - 数组形式定义属性, 只能定义 props 名
<script setup>
defineProps(["msg1", "msg2", "msg3"]);
</script>
- 数组形式定义属性, 可以定义 props 名和类型, 也可以设置默认值/必传项等
<script setup>
defineProps({
msg1: { type: String },
msg2: { type: String, default: "Default2" },
msg3: { type: String, default: "Default3", required: true },
});
</script>
父组件直接传参
例如, 父组件 src\views\DefineProps.vue
传递数据 msg1
, msg2
, msg3
<script setup>
import ChildProps from "@/components/ChildProps.vue";
import { reactive } from "vue";
const messages = reactive({
msg1: "",
msg2: undefined,
msg3: undefined,
});
const updateMessages = () => {
messages.msg1 = "msg1";
messages.msg2 = "msg2";
messages.msg3 = "msg3";
};
const doReset = () => {
messages.msg1 = "";
messages.msg2 = undefined;
messages.msg3 = undefined;
};
</script>
<template>
<div>
<ChildProps v-bind="messages" />
<button @click="updateMessages">父组件中-更新属性 msg1,msg2,msg3</button>
<button @click="doReset">父组件中-重置属性</button>
</div>
</template>
2. 组件事件
组件之间通过自定义事件进行通信是常见的一种方式。一个组件可以触发一个事件,而另一个组件监听并响应该事件。
子组件定义并抛出事件
例如, 子组件 src\components\ChildEmits.vue
触发事件 changeMsg
和 changeCount
<script setup>
import { defineEmits } from "vue";
const emit = defineEmits(["changeMsg", "changeCount"]); // 定义自定义事件
</script>
<template>
<p>
<button @click="emit('changeMsg', Math.random().toString(16).substr(2))">
子组件中-抛出事件给父级, 改变 msg 数据
</button>
<button @click="emit('changeCount', Math.random())">
子组件中-抛出事件给父级, 改变 count 数据
</button>
</p>
</template>
注意事项:
- 子组件使用
const emit = defineEmits(["changeMsg"])
和emit.changeMsg
来定义和触发自定义事件。
父组件监听事件
父组件可以监听这个事件,并执行一些逻辑:
<script setup>
import { defineProps, ref } from "vue";
import ChildEmits from "@/components/ChildEmits.vue";
defineProps({
message: { type: String, default: "default" },
count: { type: Number, default: 0 },
});
const domP1 = ref();
const domP2 = ref();
</script>
<template>
<div>
<p ref="domP1">message: {{ message }}</p>
<p ref="domP2">count: {{ count }}</p>
<ChildEmits
@changeMsg="(v) => (domP1.innerText = 'message: ' + v)"
@changeCount="(v) => (domP2.innerText = 'count: ' + v)"
/>
</div>
</template>
3. 组件插槽
插槽允许在组件内部插入内容,并将内容传递给子组件进行展示。
子组件插槽封装
子组件 src/components/ChildSlot.vue
可以通过<slot>
标签在相应的位置显示插槽内容:
<template>
<div>
<p>
<slot name="header" header="header">插槽 name=header 默认内容</slot>
</p>
<p>
<slot content="content">不具名插槽 默认内容</slot>
</p>
<p>
<slot name="footer" footer="footer">插槽 name=footer 默认内容</slot>
</p>
</div>
</template>
父组件导入插槽
例如, 在父组件 src/views/ParentSlot.vue
中导入 src/components/ChildSlot.vue
<script setup>
import ChildSlot from "@/components/ChildSlot.vue";
</script>
<template>
<div>
ChildSlot1: <ChildSlot></ChildSlot>
<br />
ChildSlot2: <ChildSlot>我是父组件传入的 内容</ChildSlot>
<br />
ChildSlot3:
<ChildSlot>
<template #header>我是父组件传入的 头部信息</template>
<span>我是父组件传入的 内容</span>
<template #footer>我是父组件传入的 脚部信息</template>
</ChildSlot>
<br />
ChildSlot4:
<ChildSlot>
<template #header="{ header }"
>我是父组件传入的 头部信息-{{ header }}</template
>
<template #default="{ content }"
>我是父组件传入的 内容-{{ content }}</template
>
<template #footer="{ footer }"
>我是父组件传入的 脚部信息-{{ footer }}</template
>
</ChildSlot>
</div>
</template>
注意事项:
子组件
- 使用
<slot></slot>
标签定义插槽的位置。 - 使用
<slot>不具名插槽默认内容</slot>
标签定义插槽的默认显示内容。 - 使用
<slot content="content">不具名插槽默认内容</slot>
标签定义插槽的参数, 父组件用#default="{ content }
获取参数content
。 - 使用
<slot name="header" header="header">插槽 name=header 默认内容</slot>
标签定义插槽的参数, 父组件用#header="{ header }"
获取参数header
。 - 使用
<slot name="footer" footer="footer">插槽 name=footer 默认内容</slot>
标签定义插槽的参数, 父组件用#footer="{ footer }"
获取参数footer
。
父组件
- 在父组件中,可以使用
<template>
标签来传递内容给子组件的插槽。 - 可以使用
#header
#default
#footer
指令来传递对应的插槽内容给子组件的插槽。 - 不具名插槽可以使用
#default
也可以省略#default
。 - 可以使用
#header="{ header }"
#default="{ content }"
#footer="{ footer }"
指令来获取子组件定义的参数header
content
footer
。
4. 依赖注入
依赖注入允许父组件将数据或函数注入到子组件中,以供子组件使用。
父组件 provide
提供数据
在父组件 src/components/ParentProvide.vue
中,我们可以使用 Provide
提供数据:
<script setup>
import ChildInject from "@/components/ChildInject.vue";
import { provide, reactive } from "vue";
// 在父组件中提供数据
const data = reactive({
message: "Hello from parent",
});
provide("data", data);
</script>
<template>
<ChildInject />
</template>
子组件 inject
获取数据
在子组件 src/components/ChildInject.vue
中,我们可以使用 inject
来获取父组件提供的数据:
<script setup>
import { inject } from "vue";
const injectedData = inject("data");
</script>
<template>
<div>{{ injectedData.message }}</div>
</template>
注意事项:
- 使用
provide
在父组件中提供数据。 - 使用
inject
在子组件中注入并使用提供的数据。
5. 异步组件
异步组件允许在需要时动态加载组件,以提高性能和加载速度。
<script setup>
import { defineAsyncComponent } from "vue";
const getAsyncData = () => {
return new Promise((resolve) => {
setTimeout(() => resolve(() => "这是一个异步组件"), 2000);
});
};
const AsyncComp1 = defineAsyncComponent(getAsyncData);
const AsyncComp2 = defineAsyncComponent({
loader: getAsyncData, // 加载函数
loadingComponent: () => "加载中...", // 加载异步组件时使用的组件
delay: 200, // 展示加载组件前的延迟时间,默认为 200ms
errorComponent: () => "加载超时...", // 加载失败后展示的组件
timeout: 1000, // 超时后会显示这里配置的报错组件,默认值是:Infinity
});
const AsyncComp3 = defineAsyncComponent({
loader: getAsyncData,
loadingComponent: () => "加载中...",
delay: 200,
errorComponent: () => "加载失败...",
timeout: 3000,
});
</script>
<template>
<div>
AsyncComp1: <AsyncComp1 />
<br />
AsyncComp2: <AsyncComp2 />
<br />
AsyncComp3: <AsyncComp3 />
</div>
</template>
注意事项:
- 使用
import()
函数异步加载组件。 - 在加载完成后,将组件赋值给相应的变量并渲染。参考 异步组件
总结: 本文涵盖了 Vue 3 中各种组件交互方式的详细代码案例,包括组件事件、组件传递 props、组件插槽、依赖注入和异步组件。通过这些示例,我们可以更好地理解和掌握 Vue 3 中组件之间的交互方式,并且注意了一些需要注意的事项。这些新的组件交互方式使我们能够以更简洁和灵活的方式构建 Vue 应用,提高开发效率和性能。