Vue3如何跨层级传递Slot
随着项目的增大,在开发过程中会遇到这样一个问题,如何跨组件传递插槽,因为在开发类似树组件的过程中,插槽需要通过外部传递到树的根节点,然后通过根节点依次传递到各个叶子节点。那么如何把根节点的Slot如传递给子组件呢?
我们在开发过程中,希望可以这样实现重新定义叶子节点的结构:
js
// 如何在dt-theme中传入一个模板,然后在dt-logo中接收到?
<dt-theme>
<dt-header>
<dt-logo>
<slot #logo></slot>
</dt-logo>
</dt-header>
</dt-theme>下面是我如何实现的,不是通过传递Slot,而是通过子节点主动去获取根节点的Slot对象,然后直接在UI中渲染出来。
首先获取根节点:
ts
// utils/get-parent-slots.ts
/**
* @param
* rootComponentName 根据父组件的name查找
* cls 根据父节点的class名称查找
*/
export function getParentSlots(rootComponentName: string = '', cls?: string) {
// 获取当前组件实例
const currentInstance = getCurrentInstance()
// 获取指定的父组件实例
const getRootComponent = (
component: ComponentInternalInstance | null
): ComponentInternalInstance | undefined => {
let clsList = component.vnode.el?.className?.split(' ') ?? []
if (component && ((rootComponentName && component.type.name === rootComponentName) || (cls && clsList.includes(cls))) ) {
return component
}
if (component && component.parent) {
const parent = component.parent
return getRootComponent(parent)
}
}
const rootCom = getRootComponent(currentInstance)
const slots = rootCom?.slots || {}
return slots
}通过递归我们可以获取到对应的父节点的所有slots,这时候我们需要一个组件来渲染暴露出来的Slot。
ts
// components/slot-container.vue
<script lang="tsx">
export default defineComponent({
name: 'slot-container',
props: {
template: {
type: Function
},
data: {
type: Object
}
},
setup(props) {
return () => {
return h('div', [props.template(props.data)])
}
}
})
</script>现在该准备的都准备好了,可以在页面中实现了。
ts
<template>
<div>
<SlotContainer v-if="slots.logo" :template="slots.logo" :data="{
testTxt: "我是传给父组件的数据"
}"></SlotContainer>
<template v-else>
<span>如果没有插槽,那么就渲染我吧!</span>
</template>
</div>
</template>
<script lang="ts" setup>
import { getParentSlots } from 'utils/get-parent-slots.ts'
import SlotContainer from 'components/slot-container.vue'
const slots = getParentSlots('dt-theme')
/**
* 也可以根据class查找
* const slots = getParentSlots(null, 'dt-theme')
*/
</script>本文使用的是Vue3的示例,也可以使用Provide/Inject来将Slot主动传递给子节点。