preface

We all know that the purpose of a component is to be reusable, we sometimes need to use this component in development, but the function of the component does not fully meet the requirements. We want to add some content to a component that developers can customize, so we can use slot slots in the component to extend and customize the component.

Slot slots in Vue give developers a more flexible way to define and extend components. Slot can be used to add additional content to a component, or slot can be used to distribute different content within the component

In this article you will learn how slot slots are used in VUE. Give you a better idea of Slot slots by implementing an example of Cell components and content distribution.

The basic use

  1. The structural content wrapped between component tags is collected by components.
  2. Passes within the component<slot>The tag introduces the collected content, and introduces the display location of the content according to<slot>Depending on the position of the marker.
  3. If not defined within the componentslotTag, the collected content will be discarded
  4. In the component<slot>Backup content can be defined between tags, and is displayed by default when no structural content is wrapped inside the external component tag
  5. Everything in the parent template is compiled in the parent scope; Everything in a subtemplate is compiled in a subscope

The structural content that wraps between component tags is called extension content

Here’s an example:

views/Slot

<template>
  <div class='container'>
    <demo data="Properties on components">
      <h1>How do you do</h1>
      <div>I am a yongcode</div>
      <p>{{msg}}</p>
      <! Data is undefined because the current template is a parent template and is compiled in the parent scope.
      <h4>{{data + ''}}</h4>
    </demo>

    <demo></demo>
  </div>
</template>

<script>
import demo from '@/components/Slot'
export default {
  data () {
    return {
      msg: 'Today I'm going to tell you about slot slots.',}},components: {
    demo
  }
}
</script>
Copy the code

components/Slot

<template>
  <div class='container'>Hello.<slot>
      <h1>This is the backup of the slot</h1>
    </slot>
  </div>
</template>

<script>
export default {
  data () {
    return {
      msg: ' '}}},</script>
Copy the code

The final effect is as follows:

More flexible use

Named slot/multi-slot usage

When we design a component, we are not sure which extensions the user will use. In VUE, different extensions are distinguished by named slots, and the corresponding extensions are controlled more flexibly.

  1. In an extension<template>The tag wraps the content of a structure, which is named (tagged) with v-slot<slot>Tag corresponding to the name attribute, directed to collect the corresponding structure content.
  2. This is not useful for extensions<template>The part wrapped in the label will be collected by the default slot as the default content.<slot>If the label does not specify name, it is regarded as the default slot.
  3. V-slot can be shortened to #
  4. <slot>Slots can be used multiple times

Here’s an example:

Three slots are defined here; Header, footer, default

views/Slot

<template>
  <div class='container'>
    <demo data="Properties on components">
      <template v-slot:footer>
        <div class="footer">I am a footer</div>
      </template>

        <div>{{msg}}</div>
      <! -- Unmarked parts are collected by default by default slot -->
      <! -- <template v-slot:default> <div> {{msg}} </div> </template> -->
      
      <template #header>
        <div class="header">I am a header</div>
      </template>
    </demo>
  </div>
</template>

<script>
import demo from '@/components/Slot'
export default {
  data () {
    return {
      msg: 'Today I'm going to tell you about slot slots.',}},components: {
    demo
  }
}
</script>
<style lang="scss" scoped>
  .footer{
    font-size: 20px;
    color: red;
  }
  .header{
    font-size: 24px;
    font-weight: 600;
  }
</style>
Copy the code

components/Slot

<template>
  <div class='container'>Hello.<slot name="header"></slot>
    <slot name="header"></slot>
    <slot></slot>
    <! -- unnamed default name default -->
    <slot name="default"></slot>
    <slot name="footer"></slot>
  </div>
</template>

<script>
export default {
  props: ['data'],}</script>
Copy the code

The effect is as follows:

Note that when declaring the contents of the slot, the slot with the same name will be overwritten

The last display is header2

<template #header>
  <div class="header">I am a header</div>
</template>
<template #header>
  <div class="header">I am a header2</div>
</template>
Copy the code

Style section for slot content

  1. Whether you define a style externally or within a component, you can make it work. It is best to write the structure wherever it is given the style.
  2. <slot></slot>The tag is not actually rendered as an HTML tag structure, but as a tag. Having the corresponding content inserted in the correct position, all the classes you define in the slot tag are useless. In principle, the slot tag itself should not affect external content; it exists only to allow external content to be inserted correctly into the specified location.

Scope slot

Sometimes we need properties within a component to control the extension content, but we can’t get the properties directly from the parent template. This is where the scope slot is needed to expose properties within the component to the external template context.

  1. In the component<slot>The extension accepts the value bound within the component by specifying a variable to the V-slot
  2. If the extension is not tagged and is only collected and used as the default, the component tag will be used by the template in the middle of the slot, so that the V-slot can be used directly on the component

Here’s an example:

views/Slot

<template>
  <div class='container'>
    <demo v-slot="slotProps">
      <div> {{msg}} </div>
      <div>{{slotProps.msg1}}</div>
      <div>{{slotProps.msg2}}</div>
    </demo>

    <! -- Use direct structure assignments for internal values -->
    <demo v-slot="{msg1,msg2}">
      <div>{{msg}} </div>
      <div>{{msg1}}</div>
      <div>{{msg2}}</div>
    </demo>
  </div>
</template>

<script>
import demo from '@/components/Slot'
export default {
  data () {
    return {
      msg: 'Today I'm going to tell you about slot slots.',}},components: {
    demo
  }
}
</script>
Copy the code

components/Slot

<template>
  <div class='container'>Hello.<slot :msg1="msg1" :msg2="msg2"></slot>
  </div>
</template>

<script>
export default {
  data () {
    return {
      msg1: 'Data within component 1'.msg2: 'Data within component 2'}}},</script>
Copy the code

The effect is as follows:

Summary and reflection

Implement Cell in VantUI

Write an extensible component with named slots and alternate contents of slots

views/Slot

<template>
  <div class="container">
    <! -- Normal use -->
    <h4>Normal use</h4>
    <demo title="Cell 1" value=Content of "1" label="Description 1" :icon="img" iconSize="30"></demo>
    <! -- Display default content -->
    <h4>Show the default content</h4>
    <demo></demo>
    <! -- Custom slot content -->
    <h4>Custom slot content</h4>
    <demo>
      <! -- Customize left icon -->
      <template #icon>
        <img width="20" height="20" src=".. /assets/logo.png" alt="">
      </template>
      <! -- Custom title section -->
      <template #title>
        <span class="title">The cell</span>
        <span class="type">Label 1</span>
      </template>
      <! -- Custom value section, multi-line overflow dot value section uses default default slot -->
      <template>
        <span
          class="value"
        >Content content content content content within the content content content content content content content content content volume content content content content content content content content content within the content content content content content content content content content content Content content Content content content content content</span>
      </template>
      <! -- Customize the right icon -->
      <template #right-icon>
        <img width="20" height="20" src=".. /assets/logo.png" alt="">
      </template>
      <! -- Custom label part of the picture text column -->
      <template #label>
        <div class="content">
          <img
            src="Https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2267599742, & FM = 26 & gp = 0. 1201218992 JPG"
            alt
          />
          <div
            class="text"
          >Vue slot slot slot slot slot slot slot slot slot slot Today we are going to introduce some ways to use vue slot slots.</div>
        </div>
      </template>
      <! -- Extra slot content -->
      <template #extra>
        <img width="20" height="20" src=".. /assets/arrow_right.png" alt="">
      </template>
    </demo>
  </div>
</template>

<script>
import img from '@/assets/logo.png';
import demo from "@/components/Slot_1";
export default {
  data() {
    return {
      msg: "Today I'm going to tell you about slot slots.",
      img
    };
  },
  components: {
    demo,
  },
};
</script>

<style lang="scss" scoped>

.title {
  font-size: 16px;
  color: #00bcd4;
}
.type {
  margin-left: 5px;
  background-color: #ee0a24;
  font-size: 10px;
  padding: 0 4px;
  color: #fff; } // let value overflow multiple lines.value {
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2; // line count -webkit-box-orient: vertical;color: # 999;
}
.content{
  display: flex;
  img{
    width: 100px;
    height: 100px;
  }
  .text{
    padding:  0 8px;
    box-sizing: border-box; }}</style>
Copy the code

components/Slot_1

<template>
  <div class="cell">
    <div class="cell_top">
      <div class="cell_top_left_box">
        <! -- Left icon -->
        <slot name="icon">
          <! -- Display the corresponding right icon property using the backup content feature of the slot -->
          <img v-if="icon" :width="iconSize" :height="iconSize" :src="icon" alt="">
        </slot>
        <! -- title -->
        <slot name="title">
          <div class="cell_top_title">{{title}}</div>
        </slot>
      </div>
      <div class="cell_top_right_box">
        <! -- value -->
        <slot>
          <div class="cell_top_value">{{value}}</div>
        </slot>
        <! -- right -->
        <slot name="right-icon"></slot>
        <! -- Extra slot -->
        <slot name="extra"></slot>
      </div>
    </div>
    <div class="label_box">
      <! -- label -->
      <slot name="label">
        <div class="cell_content">{{label}}</div>
      </slot>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    title: {
      type: String.default: "Title",},value: {
      type: String.default: "Content",},label: {
      type: String.default: "Description",},icon: {type: String.default: "",},iconSize: {type: [String.Number].default: 20}},data() {
    return {
      msg: "Data within components".msg2: "Data within component 2"}; }};</script>

<style lang="scss" scoped>
%padding{
    width: 100%;
    padding: 10px 16px;
    box-sizing: border-box;
}

.cell {
  width: 100%;
  display: felx;
  flex-direction: column;
  background-color: #fff;
  font-size: 14px;
  .cell_top {
    @extend %padding;
    display: flex;
    justify-content: space-between;
    .cell_top_left_box {
      display: flex;
      align-items: center;
      max-width: 20%;
      min-width: max-content;
      margin-right: 8px;
      .cell_top_title {
        width: 100%;
        font-size: 14px;
        color: # 333; }}.cell_top_right_box {
      display: flex;
      align-items: center;
      max-width: 78%;
      .cell_top_value {
        font-size: 14px;
        color: # 999; }}}.label_box {
    @extend %padding;
    .cell_content {
      width: 100%;
      box-sizing: border-box;
      font-size: 12px;
      color: # 999; }}}</style>
Copy the code

The effect is as follows:

PC The mobile terminal

Take advantage of slot features for content distribution

With the feature of scope slots, not only can data be distributed according to specific needs, but also functions can be better reused. The following example exposes the toDetail function externally for the jump.

views/StudentCard

<template>
  <div style="display:flex">
    <! Display personal information with this component -->
    <card>
      <! -- Students are exposed data -->
      <! -- toDetail is the exposed method, and can be ignored if you don't use a click to jump -->
      <template #default="{students}">
        <ul style="cursor:unset" v-for="student in students" :key="student.id">
          <li>Name: {{student. The name}}</li>
          <li>Student number: {{. Student id}}</li>
          <li>Class: {{student. Class}}</li>
          <li>Hobbies: {{student. Like}}</li>
        </ul>
      </template>
    </card>

    <! Display personal attributes with this component -->
    <card>
      <template #default="{students,toDetail}">
        <ul v-for="student in students" :key="student.id" @click="toDetail(student.id)">
          <li>Name: {{student. The name}}</li>
          <li>Physical: {{student. VIT}}</li>
          <li>Attack: {{student. ATK}}</li>
          <li>Defense: {{student. DEF}}</li>
        </ul>
      </template>student
    </card>
    <! -- Use this component to display personal scores -->
    <card>
      <template #default="{students,toDetail}">
        <ul v-for="student in students" :key="student.id" @click="toDetail(student.id)">
          <li>Name: {{student. The name}}</li>
          <li>Language: {{student. Performances. Chinese}}</li>
          <li>Mathematics: {{student. Performances. Math}}</li>
          <li>English: {{student. Performances. English}}</li>
        </ul>
      </template>
    </card>
  </div>
</template> <script>
import Card from "@/components/StudentCard";
export default {
  components: { 
    Card 
  },
};
</script>

<style lang="scss" scoped>
ul {
  width: 200px;
  background-color: #fff;
  border: 1px solid # 333;
  border-radius: 4px;
  padding: 15px;
  box-sizing: border-box;
  list-style: none;
  cursor: pointer;
}
</style>
Copy the code

components/Slot_2

<template>
  <div class="container">
    <! -- Bind data -->
    <slot :students="students" :toDetail="toStudentDetail">
      <ul v-for="person in students" :key="person.id">
        <li>{{person.name}}</li>
        <li>{{person.age}}</li>
      </ul>
    </slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      students: [{id: "1".name: "Zhang".class: "Class two, Three".like: "Basketball".age: "18".VIT: "200".ATK: "10".DEF: "5".performances: {
            Chinese: "90".Math: "80".English: "70",}}, {id: "2".name: "Bill".class: Class one every three years.like: "Football".age: "20".VIT: "500".ATK: "18".DEF: "10".performances: {
            Chinese: "80".Math: "85".English: "75",}}, {id: "3".name: "Zhao Wu".class: "Class 3, Grade 3".like: "Basketball".age: "20".VIT: "800".ATK: "25".DEF: "15".performances: {
            Chinese: "100".Math: "90".English: "80",},},],}; },methods: {// Bind attributes to slot to expose the method externally
    toStudentDetail(id){
      alert('Perform jump operation, current student ID is' + id)
    }
  }
};
</script>

<style lang="scss" scoped>
.container {
  width: 100%;
  background-color: #f8f8f8;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

</style>
Copy the code

The effect is as follows: