Skip to content
📈0️⃣

组件交互

组件交互

Vue3 提供了更加灵活和强大的组件交互方式。如组件事件、组件传递 props、组件插槽、依赖注入、异步组件的代码

1. 组件传递 props

一个组件可以通过 props 接收另一个组件传递的数据。

子组件定义参数

子组件 src\components\ChildProps.vue 接收来自父组件的属性 msg1, msg2, msg3

vue
<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 名
vue
<script setup>
defineProps(["msg1", "msg2", "msg3"]);
</script>
  • 数组形式定义属性, 可以定义 props 名和类型, 也可以设置默认值/必传项等
vue
<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

vue
<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>
    &nbsp;
    <button @click="doReset">父组件中-重置属性</button>
  </div>
</template>

2. 组件事件

组件之间通过自定义事件进行通信是常见的一种方式。一个组件可以触发一个事件,而另一个组件监听并响应该事件。

子组件定义并抛出事件

例如, 子组件 src\components\ChildEmits.vue 触发事件 changeMsgchangeCount

vue
<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来定义和触发自定义事件。

父组件监听事件

父组件可以监听这个事件,并执行一些逻辑:

vue
<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>标签在相应的位置显示插槽内容:

vue
<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

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 提供数据:

vue
<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 来获取父组件提供的数据:

vue
<script setup>
import { inject } from "vue";

const injectedData = inject("data");
</script>
<template>
  <div>{{ injectedData.message }}</div>
</template>

注意事项:

  • 使用provide在父组件中提供数据。
  • 使用inject在子组件中注入并使用提供的数据。

5. 异步组件

异步组件允许在需要时动态加载组件,以提高性能和加载速度。

vue
<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 应用,提高开发效率和性能。