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

相关推荐
松涛和鸣24 分钟前
Linux Makefile : From Basic Syntax to Multi-File Project Compilation
linux·运维·服务器·前端·windows·哈希算法
dly_blog29 分钟前
Vue 逻辑复用的多种方案对比!
前端·javascript·vue.js
万少44 分钟前
HarmonyOS6 接入分享,原来也是三分钟的事情
前端·harmonyos
烛阴1 小时前
C# 正则表达式:量词与锚点——从“.*”到精确匹配
前端·正则表达式·c#
wyzqhhhh1 小时前
京东啊啊啊啊啊
开发语言·前端·javascript
JIngJaneIL1 小时前
基于java+ vue助农电商系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
q_19132846951 小时前
基于Springboot+MySQL+RuoYi的会议室预约管理系统
java·vue.js·spring boot·后端·mysql·若依·计算机毕业设计
想学后端的前端工程师2 小时前
【Java集合框架深度解析:从入门到精通-后端技术栈】
前端·javascript·vue.js
VcB之殇2 小时前
git常用操作合集
前端·git
yinuo2 小时前
前端跨页面通讯终极指南⑧:Cookie 用法全解析
前端