likes
comments
collection
share

新手学Vue3(六)聊聊插槽(slot),有捷径

作者站长头像
站长
· 阅读数 13

官网的介绍,那说的是非常详细,但是一开始我看的那是一头雾水,这是个啥东东? 就好比,迷宫的地图画的非常清楚,但是能快速走出去吗?

走迷宫是有秘诀的,同理,学 slot 也一样有秘诀 —— 通过 UI库的组件学!

轻松学插槽

插槽是什么?简单的说,就是父组件给子组件传入一段html,也可以是一个组件,子组件给找个适合的位置,这样可以让子组件更灵活。

这里的子组件,指的是封装好、且不可以随意改内容的组件。

匿名插槽、默认插槽 —— 不用起名字的slot

好吧,官网并没有匿名插槽这个名字,我觉得应该给这种插槽起个名字吧,否则有点可怜的感觉。

我们还是先找个例子,其实这种例子非常多,只是我们平时没有注意到,比如 el-slectel-option,就是一种匿名插槽。

看官网API说明:element-plus.gitee.io/zh-CN/compo…

插槽名说明子标签
Option 组件列表Option Group / Option
prefixSelect 组件头部内容
empty无选项时的列表

你看,第一行的那个插槽,都没有名字,使用方法就是我们常见的那种:

  <el-select v-model="value">
    <el-option
      label="随便给个标签"
      value="标签的值"
    />
  </el-select>

是不是很熟悉,我们不知不觉间就在使用插槽。

那么我们如何做一个自己的插槽呢?

做一个带插槽的组件

  • 子组件
<template>
  <select>
    <option value="22">这是一个测试</option>
    <slot></slot>
  </select>
</template>

我们在子组件的 template 里面,设置一个 select,然后设置一个 option,再加上一个 slot 作为定位标记

这样子组件就准备好了,我们看看父组件。

父组件的使用方式

首先要引入这个子组件,然后在 template 里面这样使用:

  <my-select>
    <option value="666">外面设置的</option>
  </my-select>

这样就做好了,我们看看效果。

新手学Vue3(六)聊聊插槽(slot),有捷径

好吧,虽然不太好看,但是功能出来了。 外部设置的 option 为啥在第二行? —— 这个由子组件里的 <slot> 的位置决定的,所以我称其为定位标记

插槽可以传入任意内容,这里的 option 仅是举例。

具名插槽 —— 必须起个名字的slot

匿名插槽的使用非常简单,只是有个小问题,如果一个子组件需要很多插槽怎么办?这时候就需要给多个插槽起个名字,这样就可以区分开了。 —— 于是有了具名插槽!

这次我么来看看el-input:element-plus.gitee.io/zh-CN/compo…

插槽名说明
prefix输入框头部内容,只对非 type="textarea" 有效
suffix输入框尾部内容,只对非 type="textarea" 有效
prepend输入框前置内容,只对非 type="textarea" 有效
append输入框后置内容,只对非 type="textarea" 有效

使用示例:

<el-input v-model="input1" placeholder="Please input">
  <template #prepend>Http://</template>
</el-input>

做一个带有具名插槽的文本框

我们来尝试一下,做一个简单的具名插槽的组件:

  • 子组件:
<template>
  <slot name="prepend"></slot>
  <el-input v-model="value"></el-input>
</template>

我们建立一个子组件,在里面设置一个 el-input,在前面加上一个具名插槽的定位。

  • 父组件
 <my-input v-model="model">
   <template #prepend>http://</template>
 </my-input>

这样我们就使用了一个具名插槽,看看效果:

新手学Vue3(六)聊聊插槽(slot),有捷径

虽然传进去了,但是,在文本框的外面,不好看呀,能不能弄到里面去?

向UI库的组件转送插槽

当然可以,不过这种效果既然UI库的组件都做好了,那么我们借花献佛就好。

  • 子组件,改进一下:
<template>
  <my-input v-model="model">
    <template #prepend>
      <slot name="prepend"></slot>
    </template>
  </el-input>
</template>

这样嵌套一下就好。外面一层是 el-input 需要的具名插槽,里面的 slot 是我们设置的具名插槽。这样我们就可以把外面设置的内容,转送给 el-input。

新手学Vue3(六)聊聊插槽(slot),有捷径

是不是很神奇?

我们还可以做一个循环,把其他的插槽也都一并转送过去

    <template 
      v-for="(key, index) in slots" :key="index"
      #[key]
    >
      <slot :name="key" ></slot>
    </template>

这也是 动态插槽名称的一种应用。

作用域插槽 —— 可以父子组件传递信息的slot

因为插槽内容归父组件管理,子组件只能设置 slot 的位置,其他的事情就不让管了,但是这样缺少一点灵活性,比如子组件也需要设置一下 slot 的内容怎么办?

这个虽然做不到,但是我们可以围魏救赵,子组件把数据通过 slot 传给父组件,父组件再做后续的操作。

于是有了作用域插槽。

如果你还是晕晕的不知所谓的话(肯定的啦,你说说,你说清楚了吗?),那,也好办,我们还是看例子:

比如 el-table 的 el-column cn.vuejs.org/guide/compo…

插槽名说明
自定义列的内容 作用域参数为 { row, column, $index }
header自定义表头的内容, 作用域参数为 { column, $index }
  <el-table :data="tableData" style="width: 100%">
    <el-table-column label="Date" width="180">
      <!--匿名作用域插槽,通过 scope 接收子组件发送的信息 -->
      <template #default="scope"> 
        <div style="display: flex; align-items: center">
          <!--显示该行(row)的 date 的值 -->
          <span style="margin-left: 10px">{{ scope.row.date }}</span>
        </div>
      </template>
    </el-table-column>
  </el-table>
  • el-table 通过 props 的 data 属性,把 tableData 传给子组件(el-table-column)
  • 子组件,遍历记录集(tableData)创建 <table><tr>
  • 在遍历的过程中,把当前行的数据(row),通过 slot 的 scope 传递给父组件
  • 父组件的插槽接到后,可以做自己想做的事情了。

基本就是这个流程,所以我说 —— 作用域插槽,实现了父子组件间的一种消息传递的方式。

做一个简单的作用域插槽

作用域插槽,一般用在需要循环的地方,因为外部组件需要知道循环的是哪一行,然后才好做其他设置。

我们来动手实践一下,做一个简单的作用域插槽:

  • 子组件
<slot name="data" user="jyk" :age="18" ></slot>

先做一个具名插槽(匿名插槽也可以),然后设置 slot 的 props (user和age),然后赋值传递。

是不是很简单?如果想做遍历的话,slot 的外面可以加上 v-for 循环即可。

  • 父组件
    <nf-text >
      <template #data="{user, age}">欢迎{{user}}</template>
    </nf-text>

父组件引入这个子组件,然后设置具名插槽,用解构的方式来接收数据,然后显示。基本就是这样了。

不知道说清楚了没,反正我是已经很尽力了。。。

定义插槽的名称和属性

上面的方法并没有严格的定义一个插槽,而是很随意的拿来就用的方式,我们可以使用 Vue3.3 的新特性,规范一下。

  • 子组件
  const myslot = defineSlots<{
    default(props: any): any, // 匿名插槽
    prepend(props: any): any, // 具名插槽
    append(props: any): any, // 具名插槽
    data( // 作用域插槽
      props: {
        user: string, // 规范类型
        age: number
      }): any
  }>()

在子组件的 ts 代码里面,我们可以定义一个slot。这定义的是 slot 的名称和 props 的类型,给 template 的类型推断提供依据。

得到的 myslot 的结构是这样的:

新手学Vue3(六)聊聊插槽(slot),有捷径

基本就是这样了,总之,看看UI库是如何使用插槽的,可以便于我们理解和快速掌握。