Hi, I’m Sam-xin Lin. Vue implements a content distribution API that uses
elements as outlets to host content distribution. This is described in the Vue documentation. Specifically, a slot is a ‘space’ that allows you to add content to a component. Do you really know how a slot is inserted? I hope you can read this article as innocently and honestly as I did.
Vue Slot Basic slot usage
A single slot | anonymous slot
// Child component: (assume name: child)
<template>
<div class= 'child'>
</div>
</template>
// parent component :(reference child)
<template>
<div class= 'app'>
<child>Lin three heart</child>
</div>
</template>
Copy the code
As we know, if you add the content “Lin SAN Xin” directly to the parent component, the “Lin SAN Xin” text will not be rendered on the page. So how do we make the added content visible? Add slots to the child components.
// Child component: (assume name: child)
<template>
<div class= 'child'>
<slot></slot>
</div>
</template>
Copy the code
Compile scope (parent component in child component<slot></slot>
Insert data at
As we saw above, a slot is a ‘space’ that allows us to add content from a parent component to a child component. We can add any data value in the parent component, like this:
// parent component :(reference child)
<template>
<div class= 'app'>
<child> {{ parent }}</child>
</div>
</template>
new Vue({
el:'.app'.data: {parent:'Parent component'}})Copy the code
The syntax for using data hasn’t changed at all, but can we use data directly from the child component? Obviously not!!
// Child component: (assume name: child)
<template>
<div class= 'child'>
<slot></slot>
</div>
</template>
new Vue({
el:'child'.data: {child:'Child component'}})// parent component :(reference child)
<template>
<div class= 'app'>
<child> {{ child }}</child>
</div>
</template>
Copy the code
It is not possible to pass data directly into a child component. This is because: everything in the parent template is compiled at the parent scope; Everything in a subtemplate is compiled in the subscope.
Backup content (child components<slot></slot>
Set default values)
If I don’t add content to the parent component, then the slot will display the default value. For example:
// Child component: (assume name: child)
<template>
<div class='child'>
<slot>That's the default</slot>
</div>
</template>
Copy the code
Named slot (multiple child components<slot></slot>
Corresponding to insert content)
Sometimes, there may be more than one slot in the child component, so how do we insert the content in the parent component exactly where we want it? Just give the slot a name, that is, add the name attribute.
// Child component: (assume name: child)
<template>
<div class= 'child'>
<slot name='one'>This is the default value of 1</slot>
<slot name='two'>That's the default value of 2</slot>
<slot name='three'>That's the default value of 3</slot>
</div>
</template>
Copy the code
The parent component is added by, or slot=”name” (old syntax), v-slot:name or #name (new syntax) :
// parent component :(reference child)
<template>
<div class= 'app'>
<child>
<template v-slot:"one">This is what is inserted into the one slot</template>
<template v-slot:"two">This is what is inserted into slot two</template>
<template v-slot:"three">This is what is inserted into the three slot</template>
</child>
</div>
</template>
Copy the code
Scope slot (parent component in child component<slot></slot>
Use the child component data at
Slots allow us to add content from the parent component to the child component, and by naming slots, we can add content from more than one location. But all the data we add is in the parent component. We said above that we can’t use the data in a child component directly, but is there another way we can use the data in a child component? We can also use the slot-scope approach:
// Child component: (assume name: child)
<template>
<div class= 'child'>
<slot name= 'one' :value1='child1'>This is the default value of 1</slot>// Bind the data of child1<slot :value2='child2'>That's the default value of 2</slot>// Bind the data of child2. I didn't name the slot here</div>
</template>
new Vue({
el:'child'.data: {child1:Data '1'.child2:Data '2'}})// parent component :(reference child)
<template>
<div class='app'>
<child>
<template v-slot:one='slotone'>{{sloton.value1}} // Assign the value1 value of the child component to slotone using the syntax of V-slot</template>
<template v-slot:default='slotde'>{{slotde.value2}} // As above, because the child component does not name the slot, the default value is default</template>
</child>
</div>
</template>
Copy the code
Slot How a Slot is “inserted” (generic version)
Ordinary slot
// Child component: (assume name: child)
<template>
<div class='child'>I'm in a child component<slot></slot>
<slot name="one"></slot>
</div>
</template>
// parent component :(reference child)
<template>
<div class= 'app'>
<child>This is what is inserted into the default slot {{parent}}<template v-slot:"one">This is what is inserted into the one slot {{parent}}</template>
</child>
</div>
</template>
new Vue({
el:'.app'.data: {parent:'Parent component value'}})Copy the code
- The parent component is parsed first
child
Treat it as a child element, treat the slot aschild
In the parent scope, the parent variable value is obtained, resulting in a node that looks like this:
{
tag: "div".children: [{
tag: "child".children: ['This is the value of the parent component of the content inserted into the default slot'.'This is the value of the parent component of the content inserted into the one slot']]}}Copy the code
- Child component parsing,
slot
As a placeholder, it will be resolved into a function, roughly meaning as follows
{
tag: "div".children: [
'I'm in a child component',
_t('default'), // Anonymous slot, default name is default
_t('one') // The name of a named slot is one]}Copy the code
- The _t function needs to pass in the slot name, which defaults to
default
, the named slot is passed inname
This function is used to take the slot node obtained in the first step, and then return the resolved node. Then the node of the child component is complete, and the slot is successfully parentdiv
The label
{
tag: "div".children: ['I'm in a child component'.'This is the value of the parent component of the content inserted into the default slot'.'This is the value of the parent component of the content inserted into the one slot']}Copy the code
Scope slot
// Child component: (assume name: child)
<template>
<div class= 'child'>
<slot :value1='child1' :value2='child1'></slot>
<slot name='one' :value1='child2' :value2='child2'></slot>
</div>
</template>
new Vue({
el:'child'.data: {child1: 'subdata 1'.child2: 'Sub-data 2'}})// parent component :(reference child)
<template>
<div class='app'>
<child>
<template v-slot:default='slotde'>Insert into default slot {{slotde.value1}}{{slotde.value2}}</template>
<template v-slot:one='slotone'>Insert one slot {{sloton.value1}}{{sloton.value2}}</template>
</child>
</div>
</template>
Copy the code
- The process is complicated, but I’ll just say it in general terms. The parent component parses first, and when it encounters a scoped slot, it encapsulates that slot as a function and saves it to the child element
child
下
{
tag: "div".children: [{
tag: "child"
scopeSlots: {default (data) { // Remember the data parameter
return [Insert into one slot insert into default slot + data.value1 + data.value2]
},
one (data) { // Remember the data parameter
return ['Insert into one slot' + data.value1 + data.value2]
}
}
}]
}
Copy the code
2. It is the turn of the child component to parse. At this time, the _t function comes into play, and the child component wraps the corresponding slot data into an object and passes it to the _t function
{
tag: "div".children: [
'I'm in a child component',
_t('default', {value1: 'subdata 1'.value2: 'subdata 1'}),
_t('one', {value1: 'Sub-data 2'.value2: 'Sub-data 2'}})]Copy the code
Next is the _t internal execution, and the wrapped object is passed to the respective functions in scopeSlots as a data argument, which resolves to:
{
tag: "div".children: [
'I'm in a child component'.'Insert subdata 1 into default slot'.'Insert child data 2 into one slot']}Copy the code
$slots
Seeing this, I believe you have been clear about a process (although not very detailed), so there is a question, these nodes after parsing VNode objects, is where? You can’t parse it out and throw it away, can you? I have to find a place to store it and render the real DOM, and that place is $Slots
// Child component: (assume name: child)
<template>
<div class= 'child'>
<slot></slot>
<slot name='one'></slot>
<slot name='two'></slot>
<slot name='three'></slot>
</div>
</template>
new Vue({
el:'.child',
created () {
console.log(this.$slots) // See what's inside}})Copy the code
// parent component :(reference child)
<template>
<div class= 'app'>
<child>
<template>This is what is inserted into the default slot</template>
<template v-slot:"one">This is what is inserted into the one slot</template>
<template v-slot:"two">This is what is inserted into slot two</template>
<template v-slot:"three">This is what is inserted into the three slot</template>
</child>
</div>
</template>
Copy the code
Result of console.log:
$slots is a Map, key is the name of each slot (anonymous slot key is default), and the value corresponding to key is the VNode under each slot. You can output what VNode objects look like. I’m not going to show it here. Hey hey.