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