What is a slot
Slots, colloquically known as “slots,” occupy a place in a component template, and the contents of each slot can be specified when the component is used. This is often referred to as content distribution
It’s worth noting that the concept of slots was not introduced by Vue, but in the draft Web Components specification. To get started, see templates and slots. Vue just borrowed the idea
In Vue 2.6.0, we introduced a new uniform syntax for named slots and scoped slots (the V-slot directive). It replaces slot and slot-scope, two attributes that have been deprecated but have not been removed and are still in the document
The examples in this article are based on Vue 2.6.x, so they all use v-slot syntax.
The DEMO is all on Github and in the sandbox for you to learn. If you have any questions, please feel free to comment.
The default slot
Create a Parent component and a Child component with the following structure:
The parent component:
<! -- default slot -->
<h3>The default slot</h3>
<Child>
<div class="parent-text">Hi, I am from parent.</div>
</Child>
Copy the code
Child components:
<div class="child">
<slot></slot>
<div>Hello, I am from Child.</div>
</div>
Copy the code
When a parent calls a Child component, it passes the content from the Child tag to the
tag in the Child component, as shown below
The final render result is as follows:
<div class="child">
<div class="parent-text">Hi, I am from parent.</div>
<div>Hello, I am from Child.</div>
</div>
Copy the code
Backup the content
We can add something to the
in the child component, as shown below
<div class="child">
<slot>When the parent component doesn't pass values, I show that I'm just a backup</slot>
<div>Hello, I am from Child.</div>
</div>
Copy the code
The
tag is valid when the parent has no relevant content in the child tag, otherwise it will not be rendered.
If the parent component calls the upper face component:
<! -- Backup content -->
<h3>Backup the content</h3>
<Child1></Child1>
Copy the code
Here are the results:
A named slot
Of course, there can be more than one slot, this is mainly for flexible control of slot location and component abstraction. We can specify where to insert by setting the name attribute in the child’s slot tag, and then in the parent’s v-slot :(or using #) + the child’s name attribute value. For the default slot, v-slot:default is used
The following parent components:
<! -- named slot -->
<h3>A named slot</h3>
<Child2>
<template v-slot:footer><div>I was at the bottom of the</div></template>
<template #header><div>I am a head</div></template>
<template v-slot:default>
<div>I am content</div>
</template>
</Child2>
Copy the code
Child components
<div class="child">
<slot name="header"></slot>
<slot></slot>
<div>Hello, I am from Child.</div>
<slot name="footer"></slot>
</div>
Copy the code
It is important to note that the final rendering order is based on the order of the subcomponents, as in the above example, which is rendered as follows:
Scope slot
Sometimes we want to use child component data and events in a slot like this (note: User is the data defined in the Child3 component) :
<Child3>
<template>
<div>My name: {{user.name}}</div>
<div>My age: {{user.age}}</div>
<button @click="callMe">Clicl Me</button>
</template>
</Child3>
Copy the code
Error:
The reason for this is that the parent component cannot retrieve data from the child component. Keep in mind that everything in the parent template is compiled at the parent scope. Everything in a subtemplate is compiled in the subscope.
So how do we get the data or events of the child component? We can pass data or events directly from the child to the parent by v-bind, as shown below
<div class="child">
<div>Hello, I am from Child.</div>
<! -- pass user and callMe via V-bind -->
<slot :user="user" :callMe="callMe"></slot>
</div>
Copy the code
Then, in the slot in the parent component, it accepts the data passed by the child component with something like V-slot :default=”slotProps”
<Child3>
<! -- slotProps can be customized -->
<template v-slot:default="slotProps">
<div>My name: {{slotProps.user.name}}</div>
<div>My age: {{slotProps.user.age}}</div>
<button @click="slotProps.callMe">Clicl Me</button>
</template>
</Child3>
Copy the code
The above slotProps can be customized, and you can use the syntax for deconstructing assignments
<! -- Deconstruct assignment -->
<template v-slot:other="{ user, callMe}">
<div>My name: {{user.name}}</div>
<div>My age: {{user.age}}</div>
<button @click="callMe">Clicl Me</button>
</template>
Copy the code
Example: Decouple business logic and views
We often encounter a scenario where the business logic of two components can be reused, but the views are different. For example, we often have the requirement of switching the switch. The functions include:
- Close the switch
- Turn on the switch
- Toggle switch
- Different content when the switch is off or on
We can quickly write a JS business logic code for it:
export default {
data() {
return {
currentState: this.state
}
},
props: {
state: {
type: Boolean.default: false}},methods: {
openState() {
this.currentState = true;
},
closeState() {
this.currentState = false;
},
toggle() {
this.currentState = !this.currentState; }}}Copy the code
But maybe now my pattern one looks like this
However, there is another place where the style looks like this (just as an example, the reality may be more complicated and some buttons may even be hidden).
This is where the slot comes in handy. As mentioned above, a scope slot can pass data and events from a child component to a parent component, thereby exposing the interface. In addition, the DOM and CSS in HTML can be handed over to the parent component (caller) for maintenance. The child component can be inserted through the
tag. The main logic is as follows:
Child components:
<template>
<div class="toggle-container">
<slot :currentState="currentState" :setOn="openState" :setOff="closeState" :toggle="toggle"></slot>
</div>
</template>
Copy the code
The parent component:
<Toggle1 :state="state" class="toggle-container-two">
<template v-slot:default="{currentState, setOn, setOff, toggle }">
<button @click="toggle">switch</button>
<button @click="setOff">Shut down</button>
<button @click="setOn">Open the</button>
<div v-if="currentState">I am open to the content</div>
<div v-else>I am closing the content</div>
</template>
</Toggle1>
Copy the code
Now we are writing in a single file, but in fact the subcomponents will still have the HTML structure associated with them. How can we do this without the subcomponents having to render their own HTML? You need to understand the implementation of no render component
Advanced: implementation without rendering components
Renderless Components are components that do not need to render any of their own HTML. Instead, it only manages state and behavior. It exposes a separate scope, giving the parent or consumer complete control over what should be rendered. Vue, provides a single file component writing method. As in the above example, we are still doing some rendering in the sub-component, so how do we really not render the component?
As in the toggle example above, we have made it possible for the child component to expose a separate scope, giving the parent or consumer full control over what should be rendered. Now we need to give the single-file template structure (the div around the slot tag) to the parent, but the single-file component’s slot tag cannot be the root element of the template
At this point, we need to look at the Vue render function.
At the end of the day, Vue and all its components are just JavaScript. The single-file component is eventually extracted into a file by a build tool such as WebPack, and the rest of the content is converted to JavaScript like this:
export default {
template: <div class="mood">.</div>,
data: () = > ({ todayIsSunny: true})}Copy the code
Of course, this is not its final form; the template compiler extracts the content of the Template property and compiles it into JavaScript, which is then added to the component object through the render function. The final form should look like this:
render(h) {
return h(
'div',
{ class: 'mood' },
this.todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me')}Copy the code
The specific render function can be seen in the official website, although the cost of writing the render function is higher, but its performance is much better than a single file component.
$scopedslots.default in the render function instead of the
tag
The code is as follows:
export const toggle = {
data() {
return {
currentState: this.state
}
},
render() {
return this.$scopedSlots.default({
currentState: this.currentState,
setOn: this.openState,
setOff: this.closeState,
toggle: this.toggle,
})
},
props: {
state: {
type: Boolean.default: false}},methods: {
openState() {
this.currentState = true;
},
closeState() {
this.currentState = false;
},
toggle() {
this.currentState = !this.currentState; }}}Copy the code
This makes it possible for the sub-component to not render its own HTML at all
conclusion
This article covered some basics of Vue slots, including
- The default slot
- Backup the content
- A named slot
- Scope slot
Then, how to decouple business logic and view through slots, and then combine rendering functions to achieve true rendering free functions
The DEMO is all available on Github and in the sandbox. If you have any questions, please comment.
So hard, ask for praise, ha ha
Hope to help you ~
Recommended excellent articles in the past
- 【Vue Advanced 】 — How to implement transparent transmission of component properties?
- What the Front end should Know about HTTP
- The most powerful CSS layout – Grid layout
- How to write a complete Vue application in Typescript
- Web debugging tool the front end should know about — Whistle
Reference:
-
Vue Slot usage (easy to understand)
-
New use of slot in Vue 2.6
-
The use of functional components in Vue.js
-
Building “Renderless” Vue Components