Vue插槽-slot(含详细代码)

1,304 阅读4分钟

这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

背景

对于前端而言,slot插槽再熟悉不过了,当然我也使用过,但每次都是最基本的用法。像在某些场景中,而是直接采用了v-if去一一进行判断,从而省略了slot的使用。直到前几天,对slot插槽有了进一步的认识,才意识到插槽的用途这么大。在后续的开发过程中,也监督自己,正确使用插槽。

本次我举的例子有些生硬,但是基本意思是这样的,后期使用过程中,会使用就好啦。

Slot插槽是什么

以下述demo为例,封装了一个父组件parent.js,一个子组件child.vue;那该子组件在父组件中被调用三次,如果三个调用子组件的地方数据结构完全一致,那没问题,代码如下: parent.vue

<template>
  <div class="container">
    <child :data="data1"></child>
    <child :data="data2"></child>
    <child :data="data3"></child>
  </div>
</template>

<script>
import child from './child.vue';
export default {
  components: {
    child
  },
  data() {
    return {
      data1: ['苹果', '橘子', '梨'],
      data2: ['小明', '小丽', '小王'],
      data3: ['小狗', '小猫', '鱼'],
    }
  }
}
</script>

<style scoped>
.container {
  width: 400px;
  display: flex;
  justify-content: space-between;
}
</style>

child.vue

<template>
  <div class="child">
    <div class="container" v-for="(item, index) in data" :key="index">
      {{item}}
    </div>
  </div>
</template>

<script>
export default {
  props: {
    data: {
      type: Array,
      default: () => []
    }
  }
}
</script>

上述代码效果图如下: image.png

但是如果有一天,数据结构不一致了,比如在水果列将橘子、梨换成图片。若是以前的写法,我此时此刻会给三个调用子组件的地方再传一个参数,类型,如果传的是水果,那我会渲染为图片;或者传的参数直接就写一个对象,有图片的渲染图片,没图片的直接渲染基础数据就好了。但是如果调用组件的次数变多,细小差异又多,那将会有多个判断条件,是不合理的。 在这里也是由这个小变动吧,引出插槽,其实上述小变动,用插槽可以轻轻松松实现。 插槽其实就是一个位置,表示在这个位置,我要去写点儿其他东西。

插槽分类

  • 默认插槽
  • 具名插槽
  • 作用域插槽

默认插槽

采用默认插槽可将上述小需求加以实现,既保证公共组件不做大改动,也能在不加if判断条件的情况下,实现扩展。那可以这样来实现。 parent.vue

<div class="container">
    <child :data="data1">
      <img style="width: 50px; height:50px;" src="https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00617-2163.jpg" alt="">
    </child>
    <child :data="data2"></child>
    <child :data="data3"></child>
  </div>

child.vue

<div class="child">
    <div class="container" v-for="(item, index) in data" :key="index">
      {{item}}
    </div>
    <slot></slot>
  </div>

效果图如下:

image.png 默认插槽,也叫匿名插槽,就是简简单单一组标签。刚才已经提到了,插槽其实是一个位置,那这个slot标签就表示,这个位置我占下了,要显示我扩展的内容了。

具名插槽

具名插槽,同理,就是有名字的插槽,那他的作用是什么呢?怎么编码呢?比如,我想在上图中,在橘子上面的文字不显示苹果,要显示一个链接,或图片,那就确认显示一张苹果的图片吧,那我就在child.vue中,数据渲染的上方写个插槽,同理,我要在梨的地方显示其他内容,那我就在下方写个插槽。那我写两个插槽吗?来试一下效果。 parent.vue

<template>
  <div class="container">
    <child :data="data1">
      <img style="width: 50px; height:50px;" src="https://img1.baidu.com/it/u=1340536273,1603157826&fm=26&fmt=auto" alt="">
      <img style="width: 50px; height:50px;" src="https://img0.baidu.com/it/u=2640546467,2229219508&fm=26&fmt=auto" alt="">
    </child>
    <child :data="data2"></child>
    <child :data="data3"></child>
  </div>
</template>

child.vue

<template>
  <div class="child">
    <slot></slot>
    <div class="container" v-for="(item, index) in data" :key="index">
      {{item}}
    </div>
    <slot></slot>
  </div>
</template>

效果如图所示:

image.png 咦?是咱们想要实现的效果吗?相当于在每一个插槽中,都显示了一个苹果和一个梨的图片。这并不是咱们要的效果。那具名插槽的作用就来了,我的目的是橘子上方显示苹果图片,下方显示梨的图片。 如下图的目标图片: image.png 那就给插槽加个名字吧。 parent.vue

<div class="container">
    <child :data="data1">
      <img slot="apple" style="width: 50px; height:50px;" src="https://img1.baidu.com/it/u=1340536273,1603157826&fm=26&fmt=auto" alt="">
      <img slot="pare" style="width: 50px; height:50px;" src="https://img0.baidu.com/it/u=2640546467,2229219508&fm=26&fmt=auto" alt="">
    </child>
    <child :data="data2"></child>
    <child :data="data3"></child>
  </div>

child.vue

<div class="child">
    <slot name="apple"></slot>
    <div class="container" v-for="(item, index) in data" :key="index">
      {{item}}
    </div>
    <slot name="pare"></slot>
  </div>

作用域插槽

数据在组件自身,但根据数据生成的结构需要组建的使用者来决定

官网提到这样一句话:有时让插槽内容能够访问子组件中才有的数据是很有用的,也就是说在某些场景下,尽管父组件调用子组件的过程中有差异,但是有些时候,必须将数据放在子组件中才有用。 比如有这样的场景:有一组数据,被调用三次,第一次调用生成无序列表;第二次生成有序列表;第三次使用采用h4标题。放在以前,我又会传个参,判断一下,但是就怕调用次数多,变动大。现在好啦,采用作用域插槽,事半功倍。

这种场景将公共数据放在子组件中,在父组件中写不同的类型,那这些数据涉及到作用域,所以叫作用域插槽。相当于将数据从插槽传给父组件,有点儿类似于子组件给父组件传值。 parent.vue

<template>
  <div class="container">
    <child>
      <template scope="data">
        {{data}}
        <ul v-for="(item, index) in data.data" :key="index">
          <li>{{item}}</li>
        </ul>
      </template>
    </child>
    <child>
      <template scope="data">
        <ol v-for="(item, index) in data.data" :key="index">
          <li>{{item}}</li>
        </ol>
      </template>
    </child>
    <child>
      <template scope="data">
        <div v-for="(item, index) in data.data" :key="index">
          <h4>{{item}}</h4>
        </div>
      </template>
    </child>
  </div>
</template>

child.vue

<template>
  <div class="child">
    <slot :data="data3"></slot>
  </div>
</template>

<script>
export default {
  props: {
    data: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      data3: ['小狗', '小猫', '鱼'],
    }
  }
}
</script>

image.png 作用域插槽的写法要多加注意。

总结

插槽的作用就是让父组件可以向子组件指定位置插入HTML结构,也是一种组件间的通信方式,适用于父组件向子组件传值。