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

相关推荐
孤水寒月3 小时前
基于HTML的悬窗可拖动记事本
前端·css·html
祝余呀3 小时前
html初学者第一天
前端·html
速易达网络6 小时前
RuoYi、Vue CLI 和 uni-app 结合构建跨端全家桶方案
javascript·vue.js·低代码
耶啵奶膘6 小时前
uniapp+firstUI——上传视频组件fui-upload-video
前端·javascript·uni-app
视频砖家6 小时前
移动端Html5播放器按钮变小的问题解决方法
前端·javascript·viewport功能
lyj1689977 小时前
vue-i18n+vscode+vue 多语言使用
前端·vue.js·vscode
小白变怪兽8 小时前
一、react18+项目初始化(vite)
前端·react.js
ai小鬼头8 小时前
AIStarter如何快速部署Stable Diffusion?**新手也能轻松上手的AI绘图
前端·后端·github
墨菲安全9 小时前
NPM组件 betsson 等窃取主机敏感信息
前端·npm·node.js·软件供应链安全·主机信息窃取·npm组件投毒
GISer_Jing9 小时前
Monorepo+Pnpm+Turborepo
前端·javascript·ecmascript