这是我参与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>
上述代码效果图如下:
但是如果有一天,数据结构不一致了,比如在水果列将橘子、梨换成图片。若是以前的写法,我此时此刻会给三个调用子组件的地方再传一个参数,类型,如果传的是水果,那我会渲染为图片;或者传的参数直接就写一个对象,有图片的渲染图片,没图片的直接渲染基础数据就好了。但是如果调用组件的次数变多,细小差异又多,那将会有多个判断条件,是不合理的。 在这里也是由这个小变动吧,引出插槽,其实上述小变动,用插槽可以轻轻松松实现。 插槽其实就是一个位置,表示在这个位置,我要去写点儿其他东西。
插槽分类
- 默认插槽
- 具名插槽
- 作用域插槽
默认插槽
采用默认插槽可将上述小需求加以实现,既保证公共组件不做大改动,也能在不加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>
效果图如下:
默认插槽,也叫匿名插槽,就是简简单单一组标签。刚才已经提到了,插槽其实是一个位置,那这个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>
效果如图所示:
咦?是咱们想要实现的效果吗?相当于在每一个插槽中,都显示了一个苹果和一个梨的图片。这并不是咱们要的效果。那具名插槽的作用就来了,我的目的是橘子上方显示苹果图片,下方显示梨的图片。
如下图的目标图片:
那就给插槽加个名字吧。
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>
作用域插槽的写法要多加注意。
总结
插槽的作用就是让父组件可以向子组件指定位置插入HTML结构,也是一种组件间的通信方式,适用于父组件向子组件传值。