Vue应用——关于slot

725 阅读5分钟

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金

关于slot

闲时要有吃紧的心思,忙时要有悠闲的趣味

目录

前言

返回目录

  在实现组件多态方式重用时,我们通常会通过props传递给组件一些数据,让组件来进行展示。但是为了让这个组件具备更强的通用性,我们更希望我们组件中有部分的结构也可以由用户进行自定义操作,而不是只能自定义数据,即预先定义一些占位元素,具体结构由组件调用者自己决定。

  为此,Vue.js实现了一个内容分发API——插槽(slot)

正文

一、什么是插槽?

返回目录

  插槽,其实就相当于占位符。它在组件中给你的HTML模板占了一个位置,,让外部决定到底显示什么样的元素和内容。官网其实讲的很详细的--插槽 | Vue.js

  • 如果外部传入了插槽的内容,那么就显示外部传入的内容

  • 如果外部什么数据和元素都没有传入的时候,就不进行任何的渲染(如果设置了slot的默认值,此时会显示slot的默认值)

  插槽又分为 匿名插槽具名插槽作用域插槽

  Vue3(其实从2.6开始),我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slotslot-scope

使用场景

  通过插槽可以让用户扩展组件,去更好的复用组件和对其做定制化处理。如果父组件在使用一个复用组件的时候,获取这个组件在不同的地方都有一定的差别。可以通过slot插槽向组件的内部的指定位置传递一些内容,完成这个组件在不同场合的使用。

  比如对话框:有的兑换框需要按钮有的不需要,此时就可以用slot插槽占位,如果给它传按钮就显示按钮,如果不传就没有。

二、匿名插槽

返回目录

  即默认插槽,用slot标签来确定占位的位置,标签里面可以放默认的内容,当父组件中没有像指定的插槽插入内容,那么就使用默认的内容。

示例:

  1. 在子组件 DefaultSlots.vue 中使用匿名插槽 <slot>
  <div class="my_content">
      <p>我是默认插槽</p>
      <!-- 这里使用插槽作为占位符 -->
      <!-- 由外部调用者决定具体存放什么内容 -->
      <slot>
          <!-- 这个默认的内容只会在没有提供插入的内容时,才会显示 -->
          <p>这里是默认内容</p>
      </slot>
  </div>
  1. 在父组件 DefaultSlots.vue 中引入、注册并使用子组件 DefaultSlots,并且向插槽传入相应内容
<template>
  <h1>SlotDemo</h1>
  <div>
    <div>我是父组件</div>
    <!-- 使用匿名插槽 -->
    <DefaultSlots>
      <p>父组件传入的内容</p>
    </DefaultSlots>
  </div>
</template>
 
<script>
import DefaultSlots from '@/components/DefaultSlots.vue';

export default {
  name: 'SlotDemo',
  components: {
    DefaultSlots,
  },
  },
};
</script>

  渲染后的结果如下:

DefaultSlots.jpg

三、具名插槽

返回目录

  有时我们需要多个插槽,并且需要对每个插槽传不同的内容。此时还是用默认插槽的话,类似这样:

  <div class="my_content">
      <p>我是默认插槽</p>
      <slot />
      <slot />
      <slot />
  </div>

  这只能会使我们父元素传的内容重复展示,就像下面一样:

DefaultSlots2.jpg

  这个时候具名插糟就派上用场了。

<slot> 元素有一个特殊的 attribute: name 。我们给每个插槽起 name ,就可以让他们与要插入的内容一一对应。

  • 一个不带 name 的 slot> 出口会带有隐含的名字 default

  在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称。

示例:

  1. 在子组件 NamedSlots.vue 中使用具名插槽 ,给每个<slot>都取一个name告诉他们你的幸福……,当然,name也可以是动态的,有父组件来指定。
<template>
  <div class="my_content">
    <p>我是具名插槽</p>
    <!-- 具名插槽顾名思义就是给插槽起一个名字-->
    <slot name="header">
      <p>这里是默认header</p>
    </slot>
    <slot name="main">
      <p> 这里是默认main</p>
    </slot>
    <!-- 动态指定插槽名 -->
    <slot :name="slotName">
      <p>
        这里是默认footer
      </p>
    </slot>
  </div>
</template>
 
<script>
export default {
  name: 'NamedSlots',
  props: {
    slotName: String,
  },
};
</script>

  1. 在父组件 DefaultSlots.vue 中引入、注册并使用子组件 NamedSlots,并且向插槽传入相应内容,并使用v-slot:来指定插槽名称,告诉该内容是属于那个插槽的,当然,我们也可以使用缩写#来替代v-slot:
  <!-- 使用具名插槽 -->
  <NamedSlots :slotName="slotName">
      <!-- 没有名字的内容不会被具名插槽匹配 -->
      <p>父组件传入的内容</p>
      <template v-slot:header>
          <p>父组件传入的Header</p>
      </template>
      <!-- 跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 # -->
      <template #main>
          <p>父组件传入的Main</p>
      </template>
      <!-- v-slot:[dynamicSlotName]方式动态绑定一个名称 -->
      <template #[slotName]>
          <p>父组件传入的Footer</p>
      </template>
  </NamedSlots>

  渲染后的结果如下:

NamedSlots.jpg

四、作用域插槽

返回目录

编译作用域

  不知道你有没有想过:当你想在一个插槽中使用数据时,会绑定到父组件的数据,还是绑定到子组件的数据呢?

  假定模板为:

<child-component>
    {{message}}
</child-component

  答案是父组件,因为他们的编译作用域不一样

  组件作用域简单的说是:

  • 父组件模板的内容在父组件作用域内编译;
  • 子组件模板的内容在子组件作用域内编译。

  那么我们想让插槽内容能够访问子组件中才有的数据,应该怎么办呢?此时 作用域插槽 就排上用场了。

  这类插槽可以让父组件使用子组件的数据,只需子组件在插槽中用 v-bind (或者 : )绑定自身的数据,然后父组件在使用组件时就可以用 v-slot="slotProps" 接收,就能直接使用了。

示例:

  1. 子组件在插槽中用 v-bind (或者 : )绑定自身的数据
<template>
  <div class="my_content">
    <h3>我是作用域插槽</h3>

    <!-- 
      将子组件的数据 msg 作为 <slot> 元素的一个属性绑定上去。
      绑定在 <slot> 元素上的属性被称为 插槽 prop。
      插槽 prop可以传递插槽调用者以供使用。
    -->
    <slot :msg="msg" />

  </div>
</template>

<script>
export default {
  name: 'ScopedSlots',

  data() {
    return {
      msg: 'Msg in Child',
    };
  },
};
</script>

  1. 在父组件 用 v-slot="slotProps" 接收 插槽 prop,就能直接使用了。 注意:这里的 slotProps 可以自定义。
<template>
  <h1>SlotDemo</h1>
  <div>
    <div>我是父组件</div>

    <!--作用域插槽 -->
    <ScopedSlots>

      <!--
        // 所有传递给插槽调用者的数据(插槽 prop)都会以键值对的形式存放到slotScope中
        // 该案例中 slotScope 的值为 {msg: 'Msg in Child'},因此我们也可以直接解构解构插槽 Prop,如 v-slot="{ msg }"
        // 注意: slotScope只是一个变量的名称,可以是任意合法的JS变量名
      -->
      <template v-slot="slotScope">
        <p>{{ slotScope.msg }}</p>
      </template>

    </ScopedSlots>

  </div>
</template>
 
<script>
import ScopedSlots from '@/components/ScopedSlots.vue';

export default {
  name: 'SlotDemo',
  components: {
    ScopedSlots,
  },
  data() {
    return {
      msg: 'Msg in father',
    };
  },
};
</script>

  渲染后的结果如下:

ScopedSlots.jpg

总结

返回目录

与诸君共勉,为自己加油!

参考文档

后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址

文档协议


db 的文档库db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。