One problem encountered during development was how to pass slots across components. Because in developing tree-like components, slots need to pass externally to the root node of the tree, and then through the root node to each leaf node in turn. So how do you pass slots from the root node to child components?

During the development process, we hope to redefine the structure of the leaf node as follows:

<data-tree>
	<template v-slot:node="data">
    	<div>{{data.title}} - {{data.text}}</div>
    </template>
</data-tree>
Copy the code

How to pass Slot within a component is a problem.

Nested pass


Through the fixed level of component structure can be written directly by < V-slot… > to pass the corresponding Slot elements, layer by layer.

<data-tree>
	<data-tree-item>
      <template :node="data">
            <slot :data="data"> xxx </slot>
        </template>
    </data-tree-item>
</data-tree>
Copy the code

Slots can be passed layer by layer by creating them in the outer layer, but this becomes cumbersome if there are too many nested levels.

Render

Another option is to use the Render function to display the slot elements of the current component through $slots, and then pass the slots to the next layer when the Render function creates a new component.

h('data-tree-item', {scopedSlots: {
      	node: props= > this.$slots.node(props)
    },
})
Copy the code

This allows the Slot to be received via the Render child, and the pass is also implemented.

Dynamic components

Another way to do this is through dynamic components, which is the preferred implementation. Instead of passing Slot, children actively fetch the Slot object of the root node and render it directly in the UI.

To do this we need to create a component to render the corresponding Slot object.

First we need to get the root node:

const rootComponentName = 'data-tree'
/** * get the parent component */
const getRootComponent = (
    component: ComponentInternalInstance | null
): ComponentInternalInstance | undefined= > {
    if (component && component.type.name === rootComponentName) {
        return component
    }

    if (component && component.parent) {
        const parent = component.parent
        return getRootComponent(parent)
    }
}
Copy the code

By recursively retrieving the corresponding parent node, we can expose the Slot as Data

Setup (props) {// Get the root const dataTree = getRootComponent(getCurrentInstance()) const parentSlots = dataTree? .slots const nodeTemplate = parentSlots? .node as any return { nodeTemplate } }Copy the code

We need a component to render the exposed Slot:

components: {
        TemplateContainer: {
            functional: true.props: {
                template: {
                    type: Function
                },
                data: {
                    type: Object}},render: (props, ctx) = > h('div', [props.template(props.data)])
        }
    }
Copy the code

Now we are ready to implement the UI display:

 <template-container
                    v-if="nodeTemplate"
                    :template="nodeTemplate"
                    :data="node">
</template-container>
<template v-else>
       {{ node.label }}
</template>
Copy the code

This allows us to deliver a Slot like the one defined below, and also solves our problem of delivering slots across components.

<slot :data="node" name="node">
	 {{ node.label }}
</slot>
Copy the code

This article uses Vue 3 as an example. Vue 2 is the same concept. In Vue 3, in addition to using getRootComponent to query secondary nodes, Provide/Inject slots can be actively passed to child nodes.

For details, see vue-slot-demo

            `
Copy the code