来聊聊vue中的插槽

1,187 阅读6分钟

面试官:你来说一下 vue 中插槽的用法吧

我:(内心os:这个不是送分题吗) 是这样的,在 vue 中分为几种插槽。匿名插槽,具名插槽和作用域插槽。本质来说,都是占位符,匿名的就是直接占位,具名插槽根据名字来占位,我的理解中作用域插槽就是父组件给子级暴露出来数据,再由子组件处理后暴露给父组件使用,数据是父组件的数据,页面展示由子组件来做。比如在 Element Ui Table 组件中我们定义自己想要展示的表格数据就是使用作用域插槽来实现的。

面试官:那你知道 vue2 和 vue3 中插槽的写法有什么区别吗

我:vue 2.5及之前版本会使用slot-scop。 2.6 以后就废弃了这样的写法直接使用 v-slot 虽然写法有点改变,但实际的用法还是一样的。

面试官:那假如一个爷孙组件,我想使用一个作用插槽的功能,该如何写呢?(好像是这么问的吧,记不太清了)

我:(内心os: 父子不够你用吗,你咋这么多事呢,没完没了了还),就是这样父组件给子组件传值数据,由组件做页面展示,等于就是多了一层嘛。这个... 那个...然后越描越黑

面试官:(说的跟没说一样,这个都说不明白,你等着吧) 这...那我们今天的面试就到这里吧,我和人事沟通一下情况,后续反馈回及时通知你的

前言

不知道很多小伙伴是不是像我一样,一个知识点说起来都知道,也知道大概的用法,但是让我们表述的时候可能就无法表述清楚了,再问细节然后就支支吾吾的在尴尬中沉默了。个人认为有两种原因,一个是自己对知识点理解还处理表面状态(会用,但是不全会用),虽然能做项目,但是需要的时候,可能不能快速用它来实现我们的业务。另一方面就是就是我们的语言表达能力不是那么好,因为我们对知识点理解的不透彻,也缺少总结所以就出现了一瓶子不满,半瓶子咣当的问题。这也是我开始写文记录的一个原因,希望在自己弄明白一个点的时候也能帮助其他小伙伴去学习。如果你是大神,请绕道吧,这里不适合你。当然如果哪里聊的不对,还望大佬指点!

匿名插槽

一个没有名字或者名字叫 default 的 slot,做父子级静态数据展示。 我们先看下简单的代码实现

*父组件*
<template>
  <div>
     <p>我是父组件</p>
     <mySlot>
       <div class="is-slot">测试slot</div>
     </mySlot>
  </div>
</template>
<script setup lang="ts">
import mySlot from './my-slot.vue';
</script>

*子组件*
<template>
    <div class="my-slot">
        <slot></slot>
    </div>
</template>

一个插槽的形成,有入口还得需要有出口,在父组件中定义入口,在子组件中出口作为分发。
那如果我们在子组件中没有加入slot会怎么展示呢? image.png 那父组件中注释掉slot内容呢? image.png

经过我们测试发现无论注释掉哪种,必须有入口和出口同时存在,不然slot内容就会被忽略掉。

具名插槽

顾名思义就是有名字的插槽,如果你不写名字,而且也没有给它设置默认值,或者叫的不是它的名字,它是不会回复你的,我们来看下代码实现。

*父组件*
  <template>
  <div>
     <p>我是父组件</p>
     <mySlot>
     
      <template v-slot:header>
        <div class="is-slot">父组件header插槽</div>
      </template>
      
      <!-- <div>名字为default的匿名插槽</div> -->
      <!-- 等价于 -->
      <template v-slot:default>
        <div>名字为default的匿名插槽</div>
      </template>

      <template v-slot:footer></template>
     </mySlot>
  </div>
</template>
<script setup lang="ts">
import mySlot from './my-slot.vue';
</script>

*子组件*
<template>
    <div class="my-slot">
        <slot name="default">我是默认插槽</slot>
        <slot name="footer">我是子组件的footer插槽</slot>
        <slot name="header">我是子组件的header插槽</slot>
    </div>
</template>

前面我们用的匿名插槽也可以用default方式代替,具名插槽使用时需要在外层添加 template模板,配置名称对应的卡槽名称就可以使用了,用法也比较简单。有一个点需要注意一下,如果子组件有多个插槽的话,父组件只能决定插槽显示与否,展示顺序还是由子组件决定

作用域插槽

作用域插槽相对来前两者难理解一点。用通俗的话来说就是把子组件的数据给到父组件使用,(小孩有钱了,回馈父母,父母拿着钱随便话,想买啥买啥),子组件负责提供数据,父组件负责对内容进行展示。我们来看下简单的代码实现

*父组件*
<template>
  <div>
     <p>我是父组件</p>
     <mySlot :data="tableData">
        <template #table="{ tableScope }">
          <span >姓名-{{ tableScope.name }}; </span>
          <span >年龄-{{ tableScope.age }};</span>
          <span >职业-{{ tableScope.job }}</span>
        </template>
     </mySlot>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import mySlot from './my-slot.vue';
const tableData:any=ref([
  {name:'黄忠',age:88,job:'射手',id:1},
  {name:'李白',age:28,job:'刺客',id:2},
  {name:'妲己',age:888,job:'法师',id:3}
])
</script>

*子组件*
<template>
    <div v-for="item in data" :key="item.id">
       <slot :tableScope="item" name="table"></slot>
    </div>
</template>
<script setup lang="ts">
 defineProps({
    data:{
        default:()=>[] as any
    }
 })
</script>

image.png

上面展示的一个简易的table。父组件通过props把数据传递给子组件,然后子组件在slot中通过tableScope (随意定义),name=table是用到我们的具名插槽,如果不定义name,可以在父组件中改为 default (匿名组件) 将父组件的数据进行暴露出去提供给父组件使用。
那不能在父组件中直接使用子组件的数据吗?答案:不能 我们来看下官方的一个解释

作为一条规则,请记住: 父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

简单来说就是跟论各的,互不干涉,哪怕你是老子,不经过我的允许不能使用我的东西,除非我想给你使用。

总结一下

匿名插槽和具名插槽是父级提供数据,子组件负责展示。作用域插槽是子级提供数据,父级负责展示。作用域插槽也可以通过数据使用方面进行理解,就是作用域插槽将数据的作用域扩大了。
举个例子,匿名插槽和具名插槽是小孩小时候没钱,只能用父母的。作用域插槽是小孩长大了能挣钱了,将自己的钱给父母了,让父母随便花。(那有说,你刚才写的简易table不是父级提供数据,父级展示吗。这是props的传值和slot无关,当然这个数据你可以直接子组件定义也行)。

最后

vue 的插槽到这里就聊完了,不知道我有没有讲清楚。我也是在整理这篇文章时加深了对该知识点的理解,希望在帮助自己的同时也能帮助此时在看该文的你。我是南岸月明,致力于用最通俗易懂的话聊完一个知识点,共同学习,一起进步是我的目标!