初识Vue3(3)--插槽

48 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情

插槽

关于插槽,我之前并没有咋用过这玩意,因为我就是一搬砖的,leader都把业务给封装好的,我只需要知道需要传递哪些参数就行了,但是现在我觉得不行了,咋不可能永远一直搬砖吧?不然咋有进步呢?所以我认为我该好好看一下插槽的内容,然后试着自己也去封装封装组件,提升自己,让自己更有竞争力!

认识插槽

插槽是啥?有什么用?

根据我的学习,我认为插槽就是相当于props,只不过我们传递的不是数据,而是一堆template标签。为何说它相当于props呢?是因为props有默认值,我们也可以给子组件传递props值。而插槽也有默认值(当我们不传递标签时),但是当我们传递标签就会使用我们传递的值。第二,props可以有多个,而插槽也可以有多个,且不同的插槽也有属于自己的名字。

那插槽有啥用呢?其实也就是让我们的组件更复用、更有普适性。就像我们使用element plus或者ant design vue,他们的组件可以在不同的场景下都有用,而插槽就是这个道理,让我们的自己定义的组件也能适应不同的场景。

默认插槽

<!-- 子组件 -->
<template> 
  <div class="card">
    <slot></slot>
  </div>
</template><script lang="ts" setup></script><style scoped>
.card {
    width: 500px;
    border: 1px solid #eee;
    box-shadow: 0 0 5px #eee;
}
</style>
<!-- 父组件 -->
<template>
 <div>
  <Card>
    <h3>标题</h3>
  </Card>
 </div>
</template><script lang="ts" setup>
import { reactive, toRef, ref, toRefs, computed, watch } from "vue";
import Card from "./components/Card.vue";
​
</script>

结果呢:

image.png

我们发现,我们传给Card组件的<h3>标题</h3>就在Card组件中渲染出来了,我们也可以传递其他的不同的标签内容,然后Card渲染不同的内容。

此外,我们还可以给Card组件一个默认值,它的作用就是当Card的父组件没有给Card传递内容时,就会使用自己默认的内容。

<!-- 子组件 -->
<template> 
  <div class="card">
    <slot>默认</slot>
  </div>
</template><!-- 父组件 -->
<template>
 <div>
  <Card></Card>
 </div>
</template>

image.png

具名插槽

之前我们说过每个插槽都是有自己的名字的,默认插槽也有自己的名字,它的名字是default.

<!-- 子组件 -->
<template> 
  <div class="card">
    <slot name="default">默认</slot>
  </div>
</template><!-- 父组件 -->
<template>
 <div>
  <Card v-slot:default></Card>
 </div>
</template>

这和上面是等价的

当然我们也可以自己给插槽起名字:

<!-- 子组件 -->
<template>
  <div class="card">
    <slot name="header">默认</slot>
  </div>
</template><!-- 父组件 -->
<template>
 <div>
  <Card v-slot:header>
      <h3>标题</h3>
  </Card>
 </div>
</template>

image.png

而且Vue3还有简写形式:

<!-- 父组件 -->
<template>
 <div>
  <Card #header>
      <h3>标题</h3>
  </Card>
 </div>
</template>

我们还可以设置多个插槽,并分别给插槽名字,这样我们需要哪个插槽时就使用哪个插槽:(但是我们要注意我们不能在插槽里使用插槽,当有多个插槽时,就不能在组件跟标签上书写,我们就得使用template将其包裹然后传递给不同的插槽)

<!-- 子组件 -->
<template>
  <div class="card">
    <slot></slot>
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot name="main"></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template><!-- 父组件 -->
<template>
  <div>
    <Card>
      <template #header> 
        <h3>标题</h3>
      </template>
      <template #main> 
        <p>您好,我叫xxx,欢迎和我做朋友呀!</p>
      </template>
      <template #footer> 
        <h3>个人介绍</h3>
      </template>
    </Card>
  </div>
</template>

image.png

插槽的作用域

此外,我们需要注意的是:我们父组件传给子组件插槽的标签的作用域是 父组件,不管是css还是js。 也就是说,我们在哪定义的标签,那么这个标签的作用域就是哪。

<!-- 父组件 -->
<template>
  <div>
    <Card>
      <template #header> 
        <h3 class="aa">{{title}}</h3>
      </template>
      <template #main> 
        <p>您好,我叫xxx,欢迎和我做朋友呀!</p>
      </template>
      <template #footer> 
        <h3>个人介绍</h3>
      </template>
    </Card>
  </div>
</template><script lang="ts" setup>
import { reactive, toRef, ref, toRefs, computed, watch } from "vue";
import Card from "./components/Card.vue";
let title = '标题1'
</script><style scoped>
  .aa {
    color: red;
  }
</style>
<!-- 子组件 -->
<template>
  <div class="card">
    <slot></slot>
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot name="main"></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template><script lang="ts" setup>let title = '标题2';</script><style scoped>
.card {
  width: 500px;
  border: 1px solid #eee;
  box-shadow: 0 0 5px #eee;
  display: flex;
  flex-direction: column;
  padding: 10px 15px;
}
.header {
  border-bottom: 1px solid #ccc;
}
.footer {
  background-color: rgb(247, 247, 247);
}
.aa {
    color: blue;
}
</style>

image.png

我们发现,我们的h3标签的颜色和内容都是用的父组件的声明的内容,与子组件一点也没有关系。

插槽props

有时候我们想使用插槽内部的数据怎么办呢?我们一是可以使用老办法:emit字传父。但是插槽中有个额外的方法:可以将插槽的数据传给父组件。

<!-- 子组件 -->
<template>
  <div class="card">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot name="main" :len="[1,2,3,4,5]"></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template><!-- 父组件 -->
<template>
  <div>
    <Card>
      <template #header> 
        <h3 class="aa">{{title}}</h3>
      </template>
      <template #main="mainProps"> 
        <ul>
          <li v-for="(item,index) in mainProps.len" :key="index">{{item}}</li>
        </ul>
      </template>
      <template #footer> 
        <h3>个人介绍</h3>
      </template>
    </Card>
  </div>
</template>

image.png

这样我们就开始拿到子组件插槽的内容了。关于插槽的知识点就这么多,也不难,平时开发的确用得少,主要是没啥需求让我使用它(这里的使用是包括定义插槽、使用插槽,而不仅仅是单纯的使用插槽,那经常使用)。现在又看了一遍插槽你内容,准备这几天看看公司大佬们是如何二次封装组件的吧!!!