Slots 穿透方案-多子组件
方法 1:useSlots()
通常我们封装业务组件时一般不至于一个子组件,但多个子组件的情况下就要特别注意 Slot 命名情况了,这边分享一个在平时开发时我们选择的一个方案:使用不同前缀来区分不同 slot,props 也是同理。在 ProCard.vue 中我们加入一个 Button 组件,协商约定 c-xxx 为 Card 组件 Slot,b-xxx 为 Button 组件 Slot,这样在经过分解之后就可以区分出应该往哪个组件穿透 Slot 了。
该方法同样可以处理 Slots 夸多层级的传递的问题。
在 ProCard.vue 中取的所有 slots 并且整理好各个组件所需 slots
// 首先还是取到所有Slots的key
const slots = Object.keys(useSlots())
// 定义一个buttonSlots,用来组装需要用到的Button组件的slots
const buttonSlots = ref<string[]>([])
// 定义一个cardSlots,用来组装需要用到的Card组件的slots
const cardSlots = ref<string[]>([])
// 找出各自组件需要的slot组装好push进去就可以在template中穿透到指定组件了
for (let slotIndex = 0; slotIndex < slots.length; slotIndex++) {
const slotKey = slots[slotIndex];
if (slotKey.indexOf('c-') > -1) {
cardSlots.value.push(slotKey.slice(2, slotKey.length))
continue
}
if (slotKey.indexOf('b-') > -1) {
buttonSlots.value.push(slotKey.slice(2, slotKey.length))
}
}
接下来就可以在 template 中直接使用了
<template>
<div class="procard-container">
<PureCard @close="onEmitClose" :handleClose="handleClose">
<!-- 使用组装好的cardSlots -->
<template v-for="(slotKey, slotIndex) in cardSlots" :key="slotIndex" v-slot:[slotKey]>
<slot :name="`c-${slotKey}`">{{ slotKey }}</slot>
</template>
</PureCard>
<PureButton @click="onButtonClick" :handleClick="handleButtonClick">
<!-- 使用组装好的buttonSlots -->
<template v-for="(slotKey, slotIndex) in buttonSlots" :key="slotIndex" v-slot:[slotKey]>
<slot :name="`b-${slotKey}`">{{ slotKey }}</slot>
</template>
</PureButton>
</div>
</template>
引入一下 ProCard 组件来看一下效果吧
<template>
<div>
<ProCard title="123">
<template #c-title>
<span>CardSlot标题</span>
</template>
<template #c-default>
<span>CardSlot内容</span>
</template>
<template #c-footer>
<span>CardSlot底部</span>
</template>
<template #b-default> 按钮 </template>
</ProCard>
</div>
</template>
方法 2:render 渲染函数
例如:my-tree-table组件
<script>
import { createVNode } from 'vue';
import myTable from '@c/my-table';
/**
* 插槽穿透问题的处理方法
*/
const comTable = {
/**
* @param {*} props
* @param {*} context
* Attribute (非响应式对象,等同于 $attrs) console.log(context.attrs)
* 插槽 (非响应式对象,等同于 $slots) console.log(context.slots)
* 触发事件 (方法,等同于 $emit) console.log(context.emit)
* 暴露公共 property (函数) console.log(context.expose)
*/
setup(props, context) {
const {
parent: {
attrs,
parent: { slots }
}
} = getCurrentInstance();
/**
* createVNode接收三个参数:type,props 和 children
* type 类型:String | Object | Function
* 详细:HTML 标签名、组件、异步组件或函数式组件。使用返回 null 的函数将渲染一个注释。此参数是必需的。
* #props 类型:Object
* 详细:一个对象,与我们将在模板中使用的 attribute、prop 和事件相对应。可选。
* #children 类型:String | Array | Object
* 详细:子代 VNode,使用 createVNode() 生成,或者使用字符串来获取“文本 VNode”,或带有插槽的对象。可选。插槽也包含其中
*/
return () =>
createVNode(
myTable,
{
...attrs,
ref: 'myTable'
},
{
...slots
}
);
}
};
</script>