新手学Vue3(六)聊聊插槽(slot),有捷径
看官网的介绍,那说的是非常详细,但是一开始我看的那是一头雾水,这是个啥东东? 就好比,迷宫的地图画的非常清楚,但是能快速走出去吗?
走迷宫是有秘诀的,同理,学 slot 也一样有秘诀 —— 通过 UI库的组件学!
轻松学插槽
插槽是什么?简单的说,就是父组件给子组件传入一段html,也可以是一个组件,子组件给找个适合的位置,这样可以让子组件更灵活。
这里的子组件,指的是封装好、且不可以随意改内容的组件。
匿名插槽、默认插槽 —— 不用起名字的slot
好吧,官网并没有匿名插槽这个名字,我觉得应该给这种插槽起个名字吧,否则有点可怜的感觉。
我们还是先找个例子,其实这种例子非常多,只是我们平时没有注意到,比如 el-slect
的 el-option
,就是一种匿名插槽。
看官网API说明:element-plus.gitee.io/zh-CN/compo…
插槽名 | 说明 | 子标签 |
---|---|---|
— | Option 组件列表 | Option Group / Option |
prefix | Select 组件头部内容 | — |
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>
这样就做好了,我们看看效果。
好吧,虽然不太好看,但是功能出来了。
外部设置的 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>
这样我们就使用了一个具名插槽,看看效果:
虽然传进去了,但是,在文本框的外面,不好看呀,能不能弄到里面去?
向UI库的组件转送插槽
当然可以,不过这种效果既然UI库的组件都做好了,那么我们借花献佛就好。
- 子组件,改进一下:
<template>
<my-input v-model="model">
<template #prepend>
<slot name="prepend"></slot>
</template>
</el-input>
</template>
这样嵌套一下就好。外面一层是 el-input 需要的具名插槽,里面的 slot 是我们设置的具名插槽。这样我们就可以把外面设置的内容,转送给 el-input。
是不是很神奇?
我们还可以做一个循环,把其他的插槽也都一并转送过去
<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 的结构是这样的:
基本就是这样了,总之,看看UI库是如何使用插槽的,可以便于我们理解和快速掌握。
转载自:https://juejin.cn/post/7241790368949092389