首页 > 其他分享 >vue3 内置特殊元素<slot> 与 插槽 Slots

vue3 内置特殊元素<slot> 与 插槽 Slots

时间:2024-09-10 20:53:11浏览次数:14  
标签:slot 渲染 作用域 插槽 默认 内容 vue3 组件 Slots

vue官网
内置特殊元素<slot>
插槽 Slots

<slot>

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

  • Vue 模板里的 <slot> 元素会被编译到 JavaScript,因此不要与原生 <slot> 元素进行混淆。
  • <slot> 元素可以使用 name attribute 来指定插槽名。
    • 当没有指定 name 或者name="default"时,将会渲染默认插槽。
    • 传递给插槽元素的附加 attributes 将作为插槽 props,传递给父级中定义的作用域插槽。
    • 示例:
<!-- 默认插槽 -->
<slot></slot>

<!-- 名为content的插槽 -->
<slot name="content"></slot>

<!-- title/message将作为插槽props 传递给父组件 -->
<slot name="content" title="内容" message="插槽内容"></slot>
  • 元素本身将被其所匹配的插槽内容替换。
    如下图所示,使用 <slot> 作为一个占位符,父组件传递进来的内容就会渲染在这里。
    在这里插入图片描述

假设有一个带有插槽的<Message>组件:

<!-- 带有插槽的Message组件 -->
<template>
  <div class="msg">
    <!-- 默认插槽 -->
    <slot></slot>
  </div>
</template>

使用<Message>组件的插槽:

<template>
  <Message>Error!</Message>
</template>

最终渲染出的 DOM 如下:

<div class="msg">Error!</div>

在这里插入图片描述
通过使用插槽,<Message> 仅负责渲染外层的 <div> (以及相应的样式),而其内部的内容由父组件提供。

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

<Message>
  <Icon name="icon-error" />
  <span>Error!</span>
</Message>

为插槽指定默认内容

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

默认内容写在 <slot> 标签之间。

<!-- Button.vue -->
<button class="btn-default">
  <slot>
    确认    <!-- 默认内容 -->
  </slot>
</button>

按钮内部包含了一个<slot>元素,它作为一个占位符,用于接收父组件传递过来的内容。
如果父组件在使用这个按钮组件时没有向插槽传递任何内容,那么插槽中将显示默认的文本 “确认”。

现在,当在父组件中使用 <Button> 且没有提供任何插槽内容时:

<Button />

“确认” 将会被作为默认内容渲染:

<button class="btn-default">确认</button>

如果提供了插槽内容:

<Button>取消</Button>

那么被显式提供的内容会取代默认内容:

<button class="btn-default">取消</button>

默认插槽

当子组件中没有指定具名插槽时,父组件传入的内容会被渲染到默认插槽中。

如果子组件中只有一个未命名的<slot>元素,它就是默认插槽:

<template>
  <div>
    <h3>子组件</h3>
    <!-- 默认插槽 -->
    <slot></slot>
  </div>
</template>

父组件中可以直接在子组件标签内部传递内容,这些内容将被插入到子组件的默认插槽中:

<template>
  <div>
    <ChildComponent>
      <p>这是传递给子组件默认插槽的内容</p>
    </ChildComponent>
  </div>
</template>

具名插槽

<slot> 元素可以有一个特殊的 attribute name,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容。
显式的带 name<slot>被称为具名插槽 (named slots)。没有提供 name<slot> 出口会隐式地命名为“default”。

子组件<Layout>组件的模板如下:

<!-- 子组件 -->
<template>
  <div>
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <!-- 默认插槽,name为default -->
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

在父组件中使用<Layout> 时,使用具名插槽将多个插槽内容传入到各自目标插槽的出口:

<Layout>
  <template v-slot:header>
    <!-- header 插槽的内容放这里 -->
  </template>
  <!-- # 是 v-slot 的简写 -->
  <template #header>
    <!-- header 插槽的内容放这里 -->
  </template>
</Layout>

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

  • v-slot:header是一种明确指定插槽名称的方式,在<template>标签内部放置要插入到子组件header插槽的内容。
  • #headerv-slot:header的简写形式,同样用于指定header插槽,并在<template>标签内部放置插槽内容。

在父组件中,完整的使用<Layout> 组件:

<Layout>
  <template #header>
    <h1>这是头部内容</h1>
  </template>

  <template #default>
    <p>这是主体内容</p>
  </template>

  <template #footer>
    <p>这是底部内容</p>
  </template>
</Layout>

当一个组件同时接收默认插槽和具名插槽时,所有位于顶级的非 <template> 节点都被隐式地视为默认插槽的内容。所以上面也可以写成:

<Layout>
  <template #header>
    <h1>这是头部内容</h1>
  </template>
  
   <!-- 隐式的默认插槽 -->
  <p>这是主体内容</p>
  
  <template #footer>
    <p>这是底部内容</p>
  </template>
</Layout>

父组件通过指定插槽的名称,向不同的插槽传递了不同的内容。

条件插槽

条件插槽:根据特定条件来决定是否渲染某个插槽内容。

  1. 可以在父组件传递给子组件的插槽内容中使用v-ifv-show指令来根据条件控制插槽内容的显示。
<Layout>
  <template #header>
    <h1 v-show="showHeader">这是头部内容</h1>
    <h1 v-if="showHeader">这是头部内容</h1>
  </template>
</Layout>
<script setup lang="ts">
import { ref } from 'vue'
const showHeader = ref(true)
</script>
  1. 结合使用 $slots 属性与 v-if 来实现

子组件<Card>的模板内容如下:

<template>
  <div>
    <div v-if="$slots.title" class="card-title">
      <slot name="title" />
    </div>
    
    <div v-if="$slots.content" class="card-content">
      <slot name="content" />
    </div>
  </div>
</template>

定义了两个具名插槽 titlecontent,用于接收父组件传递的特定内容,并在相应的位置进行渲染。

通过使用v-if="$slots.title"v-if="$slots.content"分别判断是否存在名为 titlecontent 的插槽内容。只有当对应的插槽有内容时,才会渲染包含该插槽的<div>元素。

在父组件中使用子组件<Card>

<Card>
  <template #title>
    <h1>这是Card的标题</h1>
  </template>
</Card>

渲染结果如下:

<div>
  <div class="card-title">
    <h1>这是Card的标题</h1>
  </div>  
</div>

动态插槽名

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

<Layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

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

渲染作用域(传统插槽)

传统插槽是父组件向子组件传递插槽内容(父传子)。

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

<span>{{ ErrorMsg }}</span>
<Message> {{ ErrorMsg }} </Message>

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

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

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

作用域插槽

基本概念

  • 传统插槽(非作用域插槽,父传子):父组件向子组件传递静态内容,子组件在特定位置渲染这些内容。父组件无法直接访问子组件内部的数据来动态决定插槽内容的渲染方式。
  • 作用域插槽(子传父):子组件可以将数据暴露给父组件,父组件在使用插槽时可以通过解构赋值等方式获取这些数据,并根据数据动态地渲染插槽内容。

默认作用域插槽

子组件在渲染时将数据提供给插槽:可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes。

<!-- <MyComponent> 的模板 -->
<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>

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

<MyComponent v-slot="slotProps">
  {{ slotProps.text }} {{ slotProps.count }}
</MyComponent>

在这里插入图片描述
子组件传入插槽的 props 作为了 v-slot 指令的值,可以在插槽内的表达式中访问。

在父组件的模板中,也可以通过解构赋值获取子组件传递的数据:

<MyComponent v-slot="{ text, count  }">
  {{ text }} {{ count }}
</MyComponent>

具名作用域插槽

具名作用域插槽 的 props 可以作为 v-slot 指令的值被访问到,写法如下:

  1. v-slot:name="slotProps"
  2. 简写方式:#name=slotProps
  3. 可以通过解构赋值获取子组件传递的数据:v-slot:name="{ param1, param2 }"#name="{ param1, param2 }"
<!-- <MyComponent> 的模板 -->
<div>
  <slot name="header" :text="greetingMessage" :count="1"></slot>
</div>

具名插槽访问作用域插槽的 props:

<MyComponent>
  <template #header="headerProps">
    {{ headerProps.text }} {{ headerProps.count }}
  </template>
  <!-- 结构赋值的方式 -->
  <template #header="{ text, count }">
    {{ text }} {{ count }}
  </template>
</MyComponent>

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

如果同时使用了具名插槽与默认插槽,则需要为默认插槽使用显式的 <template> 标签。
直接为组件添加 v-slot 指令将导致编译错误。这是为了避免因默认插槽的 props 的作用域而困惑。

示例:

<!-- <MyComponent> template -->
<div>
  <slot :count="1"></slot>
  <slot name="footer" />
</div>

使用时,没有为默认插槽使用显式的 <template> 标签:

<!-- 该模板无法编译 -->
<MyComponent v-slot="{ count }">
  <p>{{ count }}</p>
  <template #footer>
    <!-- count 属于默认插槽,此处不可用 -->
    <p>{{ count }}</p>
  </template>
</MyComponent>

为默认插槽使用显式的 <template> 标签有助于更清晰地指出 count 属性在其他插槽中不可用:

<MyComponent>
  <!-- 使用显式的默认插槽 -->
  <template #default="{ count }">
    <p>{{ count }}</p>
  </template>

  <template #footer>
    <p>这是给底部插槽的内容</p>
  </template>
</MyComponent>

标签:slot,渲染,作用域,插槽,默认,内容,vue3,组件,Slots
From: https://blog.csdn.net/fishmemory7sec/article/details/142029431

相关文章

  • vue3快速上手和基本特性
    1)vue使用方式1)cdn方式    该方式无需使用包管理工具,只要用script标签引入js文件即可,可以快速使用vue<scriptsrc="https://unpkg.com/vue@3/dist/vue.global.js"></script>2)创建vue项目    创建vue项目时需要nodejs环境,安装好nodejs后先为nodejs换一个国......
  • 【卷起来】VUE3.0教程-05-侦听器
    =========各位看官,在开始学习之前,请帮我点个关注和赞吧========== ......
  • 工作日志:从零搭建vue3+ts+sass项目(3)
    折腾了两天后,发现elementPlus的代码都是ts的,本来不想现在用ts,因为非常不熟悉,但长痛不如短痛,重开项目!直接vue3+vite+ts+sass!让暴风雨来得更猛烈一下吧!我差那几个bug吗?1、执行命令如下:npmcreatevite@latestnpminstallnpminstallsass-Dnpmivue-router2、在src下......
  • Vue3项目开发——新闻发布管理系统(六)
    文章目录八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染4、退出功能实现①注册点击事件②添加退出功能③数据清理5、代码下载......
  • vue3 什么是Composition API 我为什么要使用它?
    CompositionAPI(组合式API)是Vue3中引入的一组全新的API,旨在提供一种更加灵活和可组合的方式来组织和复用组件逻辑。这一改变是Vue.js向函数式编程和更现代的开发方式迈出的重要一步。以下是CompositionAPI的详细解析:一、基本概念定义:CompositionAPI是一组允许开发者以函......
  • vue3常见的bug 修复bug
    Vue3作为Vue.js的最新版本,在性能、开发体验以及代码可维护性等方面带来了显著的提升。然而,就像任何软件框架一样,Vue3在使用过程中也可能遇到一些典型的bug或问题。以下是一些可能遇到的典型问题:响应式系统相关的问题:状态或数据更新不及时:由于Vue3使用Proxy来实......
  • VUE框架Vue3使用自定义的ref实现延迟加载效果的实现解决setTimeout过多导致的抖动问题
    import{customRef}from"vue";exportdefaultfunction(){//自己定义一个reffunctionuseDebouncedRef(value){//自定义的ref函数体需要符合ref规范//通过调用customRef来获取一个ref实例//调用customRef必须要给出一个回调函数作为形......
  • Vue3:具名插槽
    有时在一个组件中包含多个插槽出口是很有用的。举例来说,在一个 <BaseLayout> 组件中,有如下模板:<divclass="container"><header><!--标题内容放这里--></header><main><!--主要内容放这里--></main><footer><!--底部内容放......
  • 在vue3中手写按需加载图片
    在我们的网页中.假如使用了大量的图片,每个图片都是需要去访问加载的这就影响了我们的访问速度,手写一个按需加载组件,就可以解决这个问题让图片处于页面视图的时候再加载,减轻网页访问负担利用vue3官网给出的钩子我们常用的就是onMountent如官网所示为了及时监测,这里......
  • vue3生命周期(钩子函数)
    在Vue3中,生命周期钩子被重命名并分为了不同的阶段,以更好地描述它们的用途。这些新的生命周期钩子包括:setup():这是一个新的入口点,在beforeCreate和created之前调用。onBeforeMount/onMounted:组件挂载前/后的生命周期钩子。onBeforeUpdate/onUpdated:组件更新前/后的生命......