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' }

相关推荐
小李小李不讲道理1 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻2 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
mapbar_front2 小时前
在职场生存中如何做个不好惹的人
前端
牧杉-惊蛰3 小时前
纯flex布局来写瀑布流
前端·javascript·css
一袋米扛几楼984 小时前
【软件安全】什么是XSS(Cross-Site Scripting,跨站脚本)?
前端·安全·xss
向上的车轮4 小时前
Actix Web适合什么类型的Web应用?可以部署 Java 或 .NET 的应用程序?
java·前端·rust·.net
XiaoYu20024 小时前
第1章 核心竞争力和职业规划
前端·面试·程序员
excel4 小时前
🧩 深入浅出讲解:analyzeScriptBindings —— Vue 如何分析 <script> 里的变量绑定
前端
蓝瑟4 小时前
AI时代程序员如何高效提问与开发工作?
前端·ai编程
林晓lx5 小时前
使用Git钩子+ husky + lint语法检查提高前端项目代码质量
前端·git·gitlab·源代码管理