Vue 插槽全攻略:重塑组件灵活性

news2024/11/28 5:35:57

前言

 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!

 🍅 个人主页:南木元元


目录

什么是slot插槽

默认插槽

编译作用域

后备内容

具名插槽

作用域插槽

应用场景

动态插槽名

废弃的插槽语法

默认插槽

具名插槽

作用域插槽

总结

结语


什么是slot插槽

Vue官方文档:Vue实现了一套内容分发的 API,将<slot>元素作为承载分发内容的出口。

通俗来讲,就是“占坑”,使用<slot></slot>在子组件模板中占好位置,父组件就可以向其中传递一些内容。

Vue 的插槽可以分为三类:

  • 默认插槽
  • 具名插槽 
  • 作用域插槽

默认插槽

默认插槽又名匿名插槽,是最简单的插槽形式,当slot没有指定name属性值的时候为一个匿名插槽(一个组件内只有一个匿名插槽)。

示例:

// 子组件Child
<template>
  <div class="child">
    <!-- 默认插槽:这个内容会被父组件传递的内容替换 -->
    <slot></slot>
  </div>
</template>

// 父组件Parent(引用子组件Child)
<template>
  <Child>南木元元</Child>
</template>

渲染结果:

<template>
  <div class="child">
    南木元元
  </div>
</template>

子组件中使用默认插槽slot,实现了父组件向子组件的指定位置插入一段内容,即内容的分发。

编译作用域

我们可以通过slot向子组件传递父组件内任意的data值,比如:

// 父组件Parent(引用子组件Child)
<template>
  <Child>{{ msg }}</Child>
</template>

<script>
import Child from "./child.vue";

export default {
  components: {
    Child,
  },
  data() {
    return {
      msg: "父组件",
    };
  },
};
</script>

但我们能否直接使用子组件内的数据呢?答案是不行的。

// 子组件Child
<template>
  <div class="child">
    <!-- 默认插槽:这个内容会被父组件传递的内容替换 -->
    <slot></slot>
  </div>
</template>
<script>
export default {
  data() {
    return {
      child: "子组件",
    };
  },
};
</script>

// 父组件Parent(引用子组件Child)
<template>
  <!-- 不能访问子组件的作用域 -->
  <Child>{{ child }}</Child>
</template>

因为有一条规则:

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

后备内容

后备内容,就是slot的默认值,有时我们没有在父组件中提供内容,那么slot就会显示默认值,如:

// 子组件Child
<template>
  <div class="child">
    <!-- 父组件没有提供内容时显示后备内容 -->
    <slot>这是插槽的后备内容</slot>
  </div>
</template>

// 父组件Parent(引用子组件Child)
<template>
  <!-- 父组件没有向插槽提供内容 -->
  <Child></Child>
</template>

渲染结果: 

具名插槽

具名插槽是指带有name属性的slot,一个组件可以出现多个具名插槽。

有时需要多个插槽,我们就可以使用具名插槽,根据名称将不同内容精确分发到不同位置。

示例:

子组件

// 子组件Child
<template>
  <div class="child">
    <!-- 具名插槽:带name的slot -->
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

父组件中,需要在一个 <template> 元素上使用 v-slot:name#name(v-slot:name的缩写,2.6.0 新增)的形式向具名插槽提供内容。

// 父组件Parent(引用子组件Child)
<template>
  <Child>
    <!-- 具名插槽header -->
    <template v-slot:header>
      <h1>头部标题区</h1>
    </template>

    <!-- 这是默认插槽 -->
    <p>主要内容区</p>
    
    <!-- 具名插槽footer,使用缩写形式 -->
    <template #footer>
      <p>底部区域</p>
    </template>
  </Child>
</template>

渲染结果:

注意:上面示例中,<template>元素中的所有内容都将会被传入相应的插槽,而没有被包裹在带有v-slot的<template>中的内容都会被视为默认插槽的内容。

<p>主要内容区</p>
// 上面等价于下面这种写法
<template v-slot:default>
  <p>主要内容区</p>
</template>

作用域插槽

前面我们说过,父组件中不能直接使用子组件内的数据。如果父组件要使用子组件中的数据,我们可以使用作用域插槽。该插槽可以将子组件数据传给父组件,让父组件根据子组件的传递过来的数据决定如何渲染内容。

// 子组件Child
<template>
  <div class="child">
    <header>
      <!-- 向具名插槽中传入子组件的数据 -->
      <slot name="header" :message1="child1"></slot>
    </header>
    <main>
      <!-- 传入子组件的数据,这里没有命名slot -->
      <slot :message2="child2"></slot>
    </main>
  </div>
</template>

<script>
export default {
  data() {
    return {
      child1: "消息1",
      child2: "消息2",
    };
  },
};
</script>

// 父组件Parent(引用子组件Child)
<template>
  <Child>
    <!-- 通过v-slot的语法,将子组件的message1值赋值给headerProps -->
    <template v-slot:header="headerProps">
      {{ headerProps }}
      <br />
      {{ headerProps.message1 }}
    </template>

    <!-- 由于子组件没有给slot命名,默认值就为default -->
    <template v-slot:default="defaultProps">
      {{ defaultProps.message2 }}
    </template>
  </Child>
</template>

渲染结果:

应用场景

作用域插槽在组件封装时非常有用,它能够在保持组件逻辑封装的同时,还能允许父组件根据子组件的数据进行自定义渲染,从而在增强组件复用性和灵活性的同时保持良好的封装性。

我们来看个案例:下面封装了一个列表组件,主要负责渲染展示一个列表,数据由父组件传递。

// 子组件Child
<template>
  <div class="child">
    <h3>{{ title }}</h3>
    <p v-for="item in list" :key="item.id">{{ item.id }}.{{ item.text }}</p>
  </div>
</template>
<script>
export default {
  name: "Child",
  props: ["title", "list"],
};
</script>

// 父组件Parent(引用子组件Child)
<template>
  <Child :list="list" :title="title"> </Child>
</template>

<script>
import Child from "./child.vue";

export default {
  components: {
    Child,
  },
  data() {
    return {
      title: "名人名言",
      list: [
        {
          id: 1,
          text: "成功就是从失败到失败,也依然不改热情。",
        },
        {
          id: 2,
          text: "成为你自己想要看到的改变。",
        },
        {
          id: 3,
          text: "努力并不会背叛你,哪怕一时看不到回报。",
        },
      ],
    };
  },
};
</script>

效果:

但存在一些问题,当有些页面需要复用该组件展示列表时,比如,需要在title处增加一个图标,或者在展示内容时不显示编号1、2、3,就不够灵活。

这时,我们就可以通过作用域插槽,将列表元素内容和样式的控制权交给使用它的父组件来控制

复用该组件时在title处添加图标:

// 子组件Child
<template>
  <div class="child">
    <h3>
      <!-- 通过插槽将title传给父组件 -->
      <slot name="title" v-bind:titleValue="title"> {{ title }} </slot>
    </h3>
    <p v-for="item in list" :key="item.id">
      <!-- 通过插槽将列表元素传给父组件 -->
      <slot name="text" v-bind:itemValue="item"
        >{{ item.id }}.{{ item.text }}
      </slot>
    </p>
  </div>
</template>
<script>
export default {
  name: "Child",
  props: ["title", "list"],
};
</script>
<style scoped></style>

// 父组件Parent控制标题的显示(引用子组件Child)
<template>
  <Child :list="list" :title="title">
    <template v-slot:title="slotTitle">
      <!-- title处添加图标 -->
      <a-icon type="smile" />{{ slotTitle.titleValue }}
    </template>
  </Child>
</template>

效果:

展示内容时不显示编号1、2、3:

// 父组件Parent控制列表元素的显示(引用子组件Child)
<template>
  <Child :list="list" :title="title">
    <template v-slot:text="slotItem">
      {{ slotItem.itemValue.text }}
    </template>
  </Child>
</template>

效果:

通过作用域插槽,父组件完全控制了如何展示列表项,而不需要修改子组件的内部逻辑。

动态插槽名

2.6.0 新增:动态插槽名

动态指令参数也可以用在 v-slot 上,来定义动态的插槽名:

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

这里方括号中的dynamicSlotName会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。

废弃的插槽语法

以上的插槽语法是Vue 2.6.0后的语法,在这之前,插槽语法为slot(默认插槽或具名插槽)和slot-scope(作用域插槽)。

vue官网:v-slot指令自 Vue 2.6.0 起被引入,提供更好的支持 slot 和 slot-scope attribute 的 API 替代方案。在接下来所有的 2.x 版本中 slot 和 slot-scope attribute 仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。

默认插槽

父组件省略不写或者在template上使用slot属性:slot="default",将内容从父级传给默认插槽。

// 子组件Child
<template>
  <div class="child">
    <!-- 默认插槽:这个内容会被父组件传递的内容替换 -->
    <slot></slot>
  </div>
</template>

// 父组件Parent(引用子组件Child)
<template>
  <Child>
    <template slot="default"> 南木元元 </template>
  </Child>
</template>

具名插槽

父组件在template上使用slot属性:slot="name",将内容从父级传给具名插槽。

// 子组件Child
<template>
  <div class="child">
    <!-- 具名插槽:带name的slot -->
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

// 父组件Parent(引用子组件Child)
<template>
  <Child>
    <!-- 具名插槽header -->
    <template slot="header">
      <h1>头部标题区</h1>
    </template>

    <!-- 这是默认插槽 -->
    <p>主要内容区</p>

    <!-- 具名插槽footer -->
    <template slot="footer">
      <p>底部区域</p>
    </template>
  </Child>
</template>

作用域插槽

父组件在template上使用slot-scope属性,可以接收传递给插槽的 prop。

// 子组件Child
<template>
  <div class="child">
    <header>
      <!-- 向具名插槽中传入子组件的数据 -->
      <slot name="header" :message1="child1"></slot>
    </header>
    <main>
      <!-- 传入子组件的数据,这里没有命名slot -->
      <slot :message2="child2"></slot>
    </main>
  </div>
</template>

<script>
export default {
  data() {
    return {
      child1: "消息1",
      child2: "消息2",
    };
  },
};
</script>

// 父组件Parent(引用子组件Child)
<template>
  <Child>
    <!-- 通过slot-scope的语法,将子组件的message1值赋值给headerProps -->
    <template slot="header" slot-scope="headerProps">
      {{ headerProps.message1 }}
    </template>

    <!-- 由于子组件没有给slot命名,这里的slot="default"可以忽略为隐性写法 -->
    <template slot-scope="defaultProps">
      {{ defaultProps.message2 }}
    </template>
  </Child>
</template>

效果:

slot-scope 属性也可以直接用于非 <template> 元素 (包括组件)

<template>
  <Child>
    <!-- slot-scope attribute 也可以直接用于非 <template> 元素 -->
    <span slot-scope="defaultProps">
      {{ defaultProps.message2 }}
    </span>
  </Child>
</template>

slot-scope的值可以接收任何有效的可以出现在函数定义的参数位置上的 JavaScript 表达式,即可以在表达式中使用es6解构,这样写法就更加简洁了。

<template>
  <Child>
    <!-- 使用es6解构 -->
    <span slot-scope="{ message2 }">
      {{ message2 }}
    </span>
  </Child>
</template>

总结

最后我们来做一个小小的总结:

结语

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2192438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

医药行业的智能合同审查:大模型与AI赋能合规管理

随着医药行业的快速发展&#xff0c;尤其是在全球化背景下&#xff0c;企业在业务拓展、合作协议签订中需要处理大量复杂的合同。合同不仅是业务的法律保障&#xff0c;更是风险管理的重要工具。医药行业合同审查的复杂性源于其严格的合规性要求&#xff0c;包括与政府机构、研…

学会这几个简单的bat代码,轻松在朋友面前装一波13[通俗易懂]

大家好&#xff0c;又见面了&#xff0c;我是你们的朋友全栈君。 这个标题是干什么用的? 最近看晚上某些人耍cmd耍的十分开心&#xff0c;还自称为“黑客”&#xff0c;着实比较搞笑.他们那些花里胡哨的东西在外行看来十分nb,但只要略懂一些&#xff0c;就会发现他们的那些十…

数据库(MySQL):使用命令从零开始在Navicat创建一个数据库及其数据表(三),单表查询

前言 Navicat Premium 17 数据表需要经常清缓存&#xff0c;不然之前的自增的数据可能会一直存在&#xff0c;所以把之前的表删除重新创建是对练习数据库最简单的办法。新建数据库的命令如下&#xff1a; /* 创建有 自增主键的属性id&#xff0c;非空的属性name&#xff0c;唯…

如何使用ssm实现基于BS的超市商品管理系统的设计与实现+vue

TOC ssm787基于BS的超市商品管理系统的设计与实现vue 研究背景与现状 时代的进步使人们的生活实现了部分自动化&#xff0c;由最初的全手动办公已转向手动自动相结合的方式。比如各种办公系统、智能电子电器的出现&#xff0c;都为人们生活的享受提供帮助。采用新型的自动化…

TypeScript面向对象 02

抽象类 以abstract开头的类是抽象类。抽象类和其他类区别不大&#xff0c;只是不能用来创建对象。抽象类就是专门用来被继承的类。 抽象类中可以添加抽象方法。定义一个抽象方法使用abstract&#xff0c;没有方法体。抽象方法只能定义在抽象类中&#xff0c;子类必须对抽象方…

一些硬件知识(二十七)

单片机一般使用NOR FLASH &#xff0c;这是因为NOR FLASH支持字节级的随机读取&#xff0c;可以直接运行存贮其中的程序&#xff0c;NOR FLASH支持读取和执行存储其中的指令&#xff0c;而无需将程序拷贝到RAM中才可执行。NAND FLASH适用于大容量的数据存储&#xff0c;他的读写…

【Canvas与标志】灰座橙底红芯辐射标志

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>灰座橙底红芯辐射标志</title><style type"text/css&q…

msvcp140.dll丢失的解决方法,详细解读6种解决方法

在使用电脑时&#xff0c;我们可能会遇到提示缺少msvcp140.dll的错误信息。这个提示意味着我们的电脑中缺少MSVCP140.dll这个文件&#xff0c;它是某些程序运行所必需的。如果我们遇到这个问题&#xff0c;应该如何解决呢&#xff1f;本文将详细解析如何解决msvcp140.dll丢失的…

qemu模拟arm64环境-构建6.1内核以及debian12

一、背景 手头没有合适的arm64开发板&#xff0c;但是需要arm的环境&#xff0c;于是想到qemu模拟一个。除了硬件交互以外&#xff0c;软件层面的开发还是都可以实现的。 虚拟机还能自定义内存大小和镜像大小&#xff0c;非常适合上板前的验证&#xff0c;合适的话再买也不迟。…

深度学习:5种经典神经网络模型介绍

目录 1. LeNet&#xff1a;CNN的鼻祖 2. AlexNet&#xff1a;深度学习的开山之作 3. VGGNet&#xff1a;深度与简洁的结合 4. GoogLeNet&#xff1a;Inception模块的创新 5. ResNet&#xff1a;残差学习的革命 卷积神经网络&#xff08;CNN&#xff09;已经发展为图像识别…

张雪峰谈人工智能技术应用专业的就业前景!

一、张雪峰谈人工智能技术应用专业 在教育咨询领域&#xff0c;张雪峰老师以其深入浅出的讲解和前瞻性的视角&#xff0c;为广大学子提供了宝贵的专业选择建议。对于人工智能技术应用专业&#xff0c;张雪峰老师通常给予高度评价&#xff0c;认为这是一个充满无限可能且就业前…

DELL SC compellent存储的四种访问方式

DELL SC存储&#xff08;国内翻译为 康贝存储&#xff0c;英文是compellent&#xff09;, compellent存储是dell在大概10多年前收购的一家存储&#xff0c;原来这个公司就叫做compellent。 本文的阅读对象是第一次接触SC存储的技术朋友们&#xff0c;如何访问和管理SC存储。总…

陀螺仪LSM6DSV16X与AI集成(13)----中断获取SFLP四元数

陀螺仪LSM6DSV16X与AI集成.13--中断获取SFLP四元数 概述视频教学样品申请源码下载硬件准备SFLP开启INT中断中断读取传感器数据主程序演示 概述 本文将介绍如何通过中断机制获取 LSM6DSV16X 传感器的 SFLP&#xff08;Sensor Fusion Low Power&#xff09;四元数数据。LSM6DSV1…

深入剖析 Golang 的错误处理机制:让你的代码更加健壮与优雅

在 Go 语言中&#xff0c;错误处理是程序健壮性的重要组成部分。不同于许多其他编程语言使用的 try-catch 异常处理机制&#xff0c;Go 采用了一种更加简洁的方式&#xff0c;通过函数返回值处理错误。这种设计使得错误处理逻辑更加显式&#xff0c;代码也更容易理解和维护。 文…

Python 工具库每日推荐 【markdown2】

文章目录 引言Python文本处理库的重要性今日推荐:markdown2工具库主要功能:使用场景:安装与配置快速上手示例代码转换为HTML代码解释实际应用案例案例:博客文章处理案例分析高级特性自定义扩展安全模式命令行使用性能优化技巧扩展阅读与资源优缺点分析优点:缺点:总结【 已…

Byzantine setting 拜占庭环境

优秀教程 拜占庭将军问题(The Byzantine Generals Problem): 拜占庭将军问题(The Byzantine Generals Problem) Byzantine setting 在联邦学习和分布式计算中&#xff0c;“Byzantine setting”&#xff08;拜占庭环境&#xff09;是指一个分布式系统中的部分参与者可能表现出…

SE-Net模型实现猴痘病识别

关于深度实战社区 我们是一个深度学习领域的独立工作室。团队成员有&#xff1a;中科大硕士、纽约大学硕士、浙江大学硕士、华东理工博士等&#xff0c;曾在腾讯、百度、德勤等担任算法工程师/产品经理。全网20多万粉丝&#xff0c;拥有2篇国家级人工智能发明专利。 社区特色…

国庆出行怎么能少了这款美食

&#x1f388;国庆出游&#xff0c;怎能少了白吉饼&#xff01;&#x1f389;宝子们&#xff0c;国庆假期大家都在哪里玩耍呀&#xff1f;今天我必须给大家强烈安利国庆旅游必带美食 ——食家巷白吉饼。&#x1f60b;白吉饼看似普通&#xff0c;却有着独特的魅力。它的外表金黄…

pip install dlib 失败报错的解决方案

pip install dlib 失败报错&#xff1a; 解决方案&#xff1a; 我是按照下面三条指令按照就能解决问题 pip install cmake -i https://pypi.tuna.tsinghua.edu.cn/simple pip install boost -i https://pypi.tuna.tsinghua.edu.cn/simple pip install dlib -i https://p…

Stm32新建工程

库函数的开发方式。能够了解底层逻辑。 HAL库的开方式。快速上手&#xff0c;理解浅。 库函数的文件目录 Libraries里面就是库函数的文件 project官方实例的工程和模板&#xff0c;可以参考库函数 Utilities&#xff0c;官方在用电路测评STM32时使用的程序。 蓝色为库函数的发…