Vue3 组件的插槽

插槽 Slots

在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。通过使用插槽,可使组件更加灵活和具有可复用性。组件可以用在不同的地方渲染各异的内容,但同时还保证都具有相同的样式。

插槽内容与出口

<slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

通过使用插槽,<FancyButton> 仅负责渲染外层的 <button> (以及相应的样式),而其内部的内容由父组件提供。

插槽内容可以是任意合法的模板内容,不局限于文本。例如我们可以传入多个元素,甚至是组件:

template 复制代码
<FancyButton>
  <span style="color:red">Click me!</span>
  <AwesomeIcon name="plus" />
</FancyButton>

通过使用插槽,<FancyButton> 组件更加灵活和具有可复用性。现在组件可以用在不同的地方渲染各异的内容,但同时还保证都具有相同的样式。

渲染作用域

插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。

template 复制代码
<span>{{ message }}</span>
<FancyButton>{{ message }}</FancyButton>

这里的两个 {{ message }} 插值表达式渲染的内容都是一样的。

但插槽内容无法访问子组件的数据。Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。即:

父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。

默认内容

在外部没有提供任何内容的情况下,可以为插槽指定默认内容。

例如:

template 复制代码
//子组件
<button type="submit">
  <slot>
      --Submit-- <!-- 默认内容 -->
  </slot>
</button>
template 复制代码
//父组件
<div>
  <SubmitButton />
</div>
</button>

当父组件没有提供任何插槽内容时,父组件中的<SubmitButton>的内容会默认渲染 --Submit-- 。

如果父组件提供了插槽的内容:

template 复制代码
//父组件
<div>
  <SubmitButton>--Save--</SubmitButton>
</div>

它会取代默认内容,渲染结果为 --Save--

具名插槽

当一个组件中需要多个插槽出口 时,<slot> 元素可以有一个特殊的 attribute name,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容:

template 复制代码
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

带有name的插槽被称为具名插槽(named slots)。没有name<slot>出口会隐式地命名为"default"。

要为具名插槽传入内容时,需要使用一个含 v-slot 指令的 <template> 元素,并将目标插槽的名字传给该指令,

template 复制代码
<BaseLayout>
  <template v-slot:header>
    <!-- header 插槽的内容放这里 -->
  </template>
</BaseLayout>

v-slot 有对应的简写 #<template v-slot:header> 可以简写为 <template #header>。其意思就是"将这部分模板片段传入子组件的 header 插槽中"。

动态插槽名

动态指令参数v-slot 上也是有效的,即可以定义下面这样的动态插槽名

template 复制代码
<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- 缩写为 -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>

例:

template 复制代码
//子组件
<template>
  <div class="btn">
    <slot v-for="item in list" :name="item"></slot>
  </div>
</template>

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

  const list = ref(['one', 'two', 'three'])
 
</script>
template 复制代码
//父组件
<template>
  <myButtonVue>
    <template v-for="item in list" #[item]>
      {{ item }}
    </template>
  </myButtonVue>
</template>

<script setup lang="ts">
  import myButtonVue from "./components/myButton.vue";
  import { ref} from "vue"
  const list = ref(['one', 'two', 'three'])
</script>

作用域插槽

当在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。

可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes:

template 复制代码
<!-- <MyComponent> 的模板 -->
<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>

通过子组件标签上的 v-slot 指令,直接接收一个插槽 props 对象:

template 复制代码
<MyComponent v-slot="slotProps">
  {{ slotProps.text }} {{ slotProps.count }}
</MyComponent>

也可以在 v-slot 中使用解构:

template 复制代码
<MyComponent v-slot="{ text, count }">
  {{ text }} {{ count }}
</MyComponent>

具名作用域插槽

具名作用域插槽的工作方式也是类似的,插槽 props 可以作为 v-slot 指令的值被访问到:v-slot:name="slotProps"

template 复制代码
<slot name="header" message="hello"></slot>
template 复制代码
<MyComponent>
  <template #header="headerProps">
    {{ headerProps }}
  </template>
</MyComponent>

注意插槽上的 name 是一个 Vue 特别保留的 attribute,不会作为 props 传递给插槽。因此最终 headerProps 的结果是 { message: 'hello' }

相关推荐
恋猫de小郭29 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端