一文带你吃透Vue_slot插槽



前言

看了很多文章讲插槽,觉得不是很清晰并且年代太久了,这里给大家仔细地讲一讲,并且自vue官方2.6.0以后对具名插槽、作用于插槽的使用做出了更新,在这里这也大家做出了语法总结,我们分别在html中和vue实际项目中对插槽进行讲解


提示:文章有点长,但绝对不会让你感到枯燥,结合实例进行讲解

一、Vue插槽是什么?

插槽是子组件中的,提供给父组件使用的一个占位符, 用slot表示, 父组件可以在这个占位符中填充任何模板代码, 如HTML,组件等,填充的内容会替换子组件的slot标签;

等等等等,看这段话可能对小白有些生硬

我们来深入的了解一下

插槽就是父组件要多次渲染同一个子组件的时候,想要(抽取共性,保留不同)更改或者添加一些数据:

  • 添加
  • 替换

例如一个导航栏,我们在切换不同页面时,导航栏是略有不同的,比如图标,样式,但他们调用的是相同的组件,我们要如何做到修改呢

我们先以简单的实例对插槽进行讲解

二、插槽的简单示例

1.slot插槽


请看:

  1. Vue实例是父组件,我们在其中注册一个名为cpn的子组件
  2. 我们使用了四次cpn这个组件,想要在cpn标签的位置添加或修改一些东西,可并不能达到我们的效果

1611846372555.png

我们在子组件中添加slot属性

在这里插入图片描述

再来看,我们在cpn组件中写的内容替换掉了slot,这里由于写法死板,只有我们上面提到的添加属性(保留了组件原本内容,在slot位置进行添加要替换的内容)

在这里插入图片描述

所以你是否可以理解

插槽就是父组件想要多次渲染同一个子组件的时候,想要(抽取共性,保留不同)更改或者添加一些数据

2.具名插槽

有时候我们想要单独的修改众多插槽中的一个元素,这就用到了 具名插槽

还是以上面实例讲解

我们想要 单独的修改其中一个,发现根本不行,这时候我们就要使用具名插槽,给插槽 定义name属性来做到具名的效果

在这里插入图片描述

防止与普通插槽混淆,先定义name属性,然后我们在app父组件中使用时仍然采用普通插槽的写法,是不是起不到相应的效果,cpn组件会把内容全部替换

在这里插入图片描述



再看这时,我们先来梳理一下这个结构

这里我们在子组件定义了三个name属性,在父组件中使用了三次cpn这个子组件所以效果有三行,注意span标签是行内元素,我们一行一行来看

  • 第一个cpn:

我们分别定义了两个left、center属性,我们发现slot属性值和name值如果相同会替换掉slot标签,而name="right"这个属性在app父组件并没有定义,所以就保留了下来

  • 第二个cpn

我们在父组件只定义了slot=“center”,由于没有写值所以就是空替换了cpn里的center,所以不显示,只保留了左边、右边

  • 第三个cpn:

亦是如此,你是否发现了规律

在这里插入图片描述

相同的属性值会单独的进行替换,不同的则会被保留,具名插槽的作用就是根据名称进行特定的修改

3.作用域插槽

一句话:
作用域插槽:

让插槽内容能够访问子组件中才有的数据

前面的具名插槽可以我们能够按自己想法修改其中的部分插槽,可如果子组件中有数据,我们同样想把数据在父组件上渲染,这时就用到了作用域插槽
代码如下(示例):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
  <cpn>
      <template v-slot:default="slotPreps">
        {{slotPreps.preps}}
      </template>
  </cpn>
</div>
<template id="cpn">
  <div>
    <slot v-bind:preps="preps">
      <ul>
        <li v-for="item in preps">{{item}}</li>
      </ul>
    </slot>
  </div>
</template>
<script src="../JS/vue.js"></script>

<script>
  const cpn={
    template:`#cpn`,
    data(){
      return{
        preps:['蜘蛛侠','美队','钢铁侠','黑寡妇','超人']
      }
    }
  }
  const app = new Vue({
    el:'#app',
    data:{
      message:'VUe开始了',
    },
    components:{
      cpn
    },
    method:{
    }
  })
</script>

</body>
</html>

讲解

我们可以看到定义在子组件cpn里的数组preps

在这里插入图片描述

在父组件里使用一次cpn组件

在这里插入图片描述

这时候我们想要换一种渲染格式或者显示部分数据就遇到了难题,数据是子组件的,我们想要在父组件中修改并渲染就必须拿到这个preps数组

作用域插槽对我们很友好

原有的slot和slot-scope都被v-slot代替,可能大家会不适应,但确实简化了一些复杂语法

vue官方的更新和废弃的插槽语法可以看一下

v-slot:你想定义的名称="vue给出的作用域的映射(他会直接映射到你v-bind后面的内容)"

在这里插入图片描述

三、slot语法的废弃和更新

vue官方的更新和废弃的插槽语法可以看一下
下面也在讲,如果觉得官方的晦涩难懂就继续往下看就好

更新废弃整理:

废弃的具名插槽:

在 template标签上使用特殊的 slot 如

 <template slot="header">
    <h1>Here might be a page title</h1>
  </template>

或者直接把 slot attribute 用在一个普通元素上:

<h1 slot="header">Here might be a page title</h1>
更新为
		<cpn>
          <template v-slot:item-icon>
            <i  class="fa fa-shopping-cart"></i>
          </template>
          <template v-slot:item-text>
            <div >首页</div>
          </template>
        <cpn>

slot更新为v-slot并且需要tempalte外包

废弃的作用域插槽:

在 上使用特殊的 slot-scope attribute

<slot-example>
  <template slot="default" slot-scope="slotProps">
    {{ slotProps.msg }}
  </template>
</slot-example>

slot-scope attribute 也可以直接用于非 元素 (含组件)

<slot-example>
  <span slot-scope="slotProps">
    {{ slotProps.msg }}
  </span>
</slot-example>
更新为
<cpn>
      <template v-slot:default="Preps">
        <div>{{Preps.preps}}</div>
        <div>{{Preps.preps[1]}}</div>
      </template>
  </cpn>

slot-scope更新为v-slot并且需要tempalte外包
`


四、Vue项目中插槽的使用

由于Vue官方在2.0.6版本后做出了更新,我们想要在Vue项目中使用具名插槽,我们需要用v-slot并且用template标签包起来

<template v-slot:item-icon>
            <i  class="fa fa-shopping-cart"></i>
          </template>
          <template v-slot:item-text>
            <div >首页</div>
          </template>



所以我们讲讲在Vue项目中如何使用具名插槽进行模板组件抽离,使其变得便于修改和维护,我们以tabbar导航栏为例

效果图

在这里插入图片描述

//这里先给上源码,一共三个文件,App.vue  TabBar.vue  TabBarItem.vue

其实真正的项目App.vue里是很干净的,这里写了这么多是因为层级关系少一点方便大家理解,并且我们主要是讲具名插槽的,如果想,完全可以再把App.vue的东西抽离出去在封装成别的组件

TabBar作为整体

在这里插入图片描述

TabBarItem
在这里插入图片描述

目录结构

在这里插入图片描述

App.vue

<template>
  <div id="app">
      <tab-bar>
        <tab-bar-item>
          <template v-slot:item-icon>//图标
            <i  class="fa fa-shopping-cart"></i>
          </template>
          <template v-slot:item-text>//文本
            <div >首页</div>
          </template>
        </tab-bar-item>
        <tab-bar-item>
          <template v-slot:item-icon>
            <i  class="fa fa-align-justify"></i>
          </template>
          <template v-slot:item-text>
            <div >分类</div>
          </template>
        </tab-bar-item>
        <tab-bar-item>
          <template v-slot:item-icon>
            <i  class="fa fa-shopping-cart"></i>
          </template>
          <template v-slot:item-text>
            <div >购物车</div>
          </template>
        </tab-bar-item>
        <tab-bar-item>
          <template v-slot:item-icon>
            <i  class="fa fa-google-wallet"></i>
          </template>
          <template v-slot:item-text>
            <div>我的</div>
          </template>
        </tab-bar-item>
      </tab-bar>
  </div>
  <router-view/>
</template>

<script>
import TabBar from './components/TabBar/TabBar'
import TabBarItem from './components/TabBar/TabBarItem';
  export default {
    name:'App',
    components:{
      TabBar,
      TabBarItem
    }
  }
</script>
<style>
  /*style里面引用的固定格式*/
  @import "assets/css/base.css";

</style>

base.css想加可以加上

在这里插入图片描述

TabBar

<template>
  <div id="tab-bar">
<!--    父组件中的内容会替换掉slot,就是App.vue中的tab-bar-item相当于在slot的位置-->
    <slot></slot>
  </div>
</template>

<script>

export default {
  name: "TabBar"
}
</script>

<style scoped>

#tab-bar{
  display:flex;
  background-color: #f6f6f6;
  /*固定底部*/
  position:fixed;
  left:0;
  right:0;
  bottom:0;
  box-shadow: 0px  0px 10px -5px
}

</style>

TabBarItem

<template>
  <div class="tab-bar-item">
    <slot name="item-icon"></slot>
    <slot name="item-text"></slot>
  </div>
</template>

<script>
export default {
name: "TabBarItem"
}
</script>

<style scoped>
.tab-bar-item{
  /*水平分布,居中*/
  flex:1;
  text-align:center;
  /*移动端的tabbar高度一般都为49px;*/
  height:49px;

}
</style>






因为要把组件抽离出来方便修改,所以在这里不带着大家一步步讲解,我们主要是讲Vue项目中的具名插槽

我们可以看到这里的关系,TabBar和TabBarItem同样都作为App.vue这个父组件的子组件

我们在App.vue中使用了一次tab-bar组件四次tab-bar-item组件,也就是一个整体导航栏和四个item,想要添加和修改是不是变得非常简单

在这里插入图片描述

还记得我们的slot插槽吗,这里是普通插槽,定义在子组件中,也就是父组件中的tab-bar组件会替换掉slot

所以你是否明白,这里如果不搞这么多封装,把四个tab-bar-item组件写在slot标签的位置是一个效果

在这里插入图片描述

我们来具体的看看item组件,我们定义了两个具名插槽,分别是图标和文本,整体包裹一个class选择器用于修改样式

在这里插入图片描述

我们回到App.vue,我们定义v-slot的属性值和item子组件中name值保持一致就会进行替换,来达到每个Item都有不同的图标和名称,进而对子组件进行渲染

在这里插入图片描述

你懂了吗?

Logo

前往低代码交流专区

更多推荐