插槽(slot)透传在写组件和二次封装时尤为有用,我们总希望保持组件内部的插槽在二次封装后仍然提供使用。一个经典的开发背景:二次封装UI组件库的<table>组件(如ElementUI,Arco-Design,Ant-Design,巴拉巴拉)封装后期望自定义TableColumn仍然能够在业务页面能够通过slot传入。
开始
下面将以此为例进行讲解如何实现:(在vue3+setup下)
所有内容均在vue.js官方文档由更详细的说明:slot-插槽
原理图
语法基础
透传插槽需要了解及使用如下基础语法
- 1.定义插槽及透传数据
<slot name="插槽名" :attr1="parentData1" :attr2="parentData2"></slot> - 2.使用插槽
<template #插槽名></template> - 3.动态插槽
<template #[dynamicSlotName]> </template>- 注意:动态名称有个中括号
- 4.获取插槽
useSlots见官方文档 useSlots<script setup> import { useSlots, useAttrs } from 'vue' const slots = useSlots() </script>
Code it
下方将以 Arco-Design 的table进行二次封装为例。
并不会将全部code展示,只会展示关键code
一、arco的table组件
如果你使用的是Element-UI 其实差不多,参考即可
详见Arco关于table的自定义渲染
<a-table :columns="columns" :data="data">
<template #optional="{ record }">
<a-button @click="$modal.info({ title:'Name', content:record.name })">view</a-button>
</template>
</a-table>
const columns = [{
title: 'Name',
dataIndex: 'name',
}, {
title: 'Salary',
dataIndex: 'salary',
}, {
title: 'Address',
dataIndex: 'address',
}, {
title: 'Email',
dataIndex: 'email',
}, {
title: 'Optional',
slotName: 'optional' // 看这个slotName 和 <template> 上的插槽名一致
}];
二、二次封装custom-table组件
// @/components/custom-table/index.vue
<template>
<a-table
:columns="props.columns"
:data="dataList"
>
<!-- ⭐️关键在这里 ⭐️
1.遍历插槽
2.使用 #[]来动态渲染插槽
3.使用slot再定义一个同名插槽 (从业务页面接受custom-table的插槽)
-->
<template v-for="slot in columnSlots" :key="slot.name" #[slot.name]="{ record }">
<slot :name="slot.name" :record="record"></slot>
</template>
</a-table>
</template>
<script setup lang="ts">
import { useSlots, computed } from 'vue';
const slots = useSlots();
// ⭐️下面的computed 按各自需求处理就好
const curSlotName = ['需要排除掉的slotName,即表示无需透传的slot'];
const columnSlots = computed(() => {
if (slots) {
return Object.keys(slots)
.filter((key) => !curSlotName.includes(key))
.map((t) => {
return { name: t, slot: slots[t] };
});
}
return [];
});
</template>
三、在业务页面使用
// 自己的业务页面
<template>
<custom-table ref="tableRef" :columns="columns" :fetch-api="list">
<!-- ⭐️这里只是示意 : slotName="createTime" -->
<template #createTime="{ record }">
{{ dayjs(record.createTime).format('YYYY-MM-DD') }}
</template>
</custom-table>
</template>
<script setup lang="ts">
import { list} from '@/api/article';
import customTable from '@/components/table/index.vue';
const columns = [
{
title: '标题',
dataIndex: 'title',
ellipsis: true,
tooltip: true,
width: 200,
},
{
title: '更新时间',
dataIndex: 'createTime',
slotName: 'createTime', # ⭐️定义slotName
ellipsis: true,
tooltip: true,
}
];
</script>