咱们一块儿学习Vue中的 slot 吧。

Vue 2.6 已经发布一段时间了,主要的更新就是slot(插槽)。可能不少人从始至终都没用过slot,那多是你对它不够了解,当你真正的了解它的时候,你就会知道当你封装一个可复用插件的时候它是多么的 perfect~html

代码已放到Github上。能够下载跟着练习一下。vue


咱们先看一个简单的例子:git

<!--子组件-->
<template>
    <button class="custom-button">
        <slot></slot>
    </button>
</template>
<style>
.custom-button{
    color: #fff;
    background-color: #409eff;
    padding: 10px 20px;
    font-size: 14px;
    border-radius:6px;
    outline: none;
    border: 1px solid #dcdfe6;
}
</style>
复制代码
<!--父组件-->
<template>
    <div class="button-list">
        <cus-bottom>肯定</cus-bottom>
    </div>
</template>
<script>
import customButton from './customButton.vue'
export default{
    components:{
        'cus-bottom':customButton
    }
}
</script>

复制代码

最终渲染:github

<div class="button-list">
    <button class="custom-button">
        肯定
    </button>
</div>
复制代码

当子组件渲染的时候,<slot></slot> 将会被替换为“肯定”。slot是不会被渲染的,它是用来接收父组件传过来的内容。bash

咱们来看一张图,我以为更容易理解插槽的概念:ide

游戏卡就是父组件传给的内容,插口就至关于子组件的 slot标签,组合起来就是最终的渲染。

固然上述就是最简单的slot的使用,咱们接着往下看。ui

插槽内容

<slot></slot>不只能够接受字符串,还能够接收Html模板:spa

<!--父组件-->
<div class="button-list">
    <cus-bottom>
        <span>肯定</span>
    </cus-bottom>
</div>
复制代码

最终渲染:插件

<div class="button-list">
    <button class="custom-button">
        <span>肯定</span>
    </button>
</div>
复制代码

还能够接收其余组件:code

<!--父组件-->
<div class="button-list">
    <cus-bottom> 
        <!-- cus-font 图标组件 -->
        <cus-font></cus-font>
        <span>肯定</span>
    </cus-bottom>
</div>
复制代码

编译做用域

父级模板里的全部内容都是在父级做用域中编译的;子模板里的全部内容都是在子做用域中编译的。

咱们用例子来解释一下什么叫 编译做用域

<!--父组件-->
<template>
    <div class="button-list">
        <cus-bottom>{{buttonText}}</cus-bottom>
    </div>
</template>
<script>
import customButton from './customButton.vue'
export default{
    components:{
        'cus-bottom':customButton
    },
    data(){
        return{
            buttonText:'保存'
        }
    }
}
</script>
复制代码

{{buttonText}}是父组件中的编译的,因此子组件获取不到buttonText变量,反之在子组件内编译的变量父组件也获取不到。

默认内容

官网叫后备内容。我以为有点怪,这里就叫默认内容。

当咱们button默认内容就是“肯定”:

<button class="custom-button">
    <!--这里的肯定就是默认内容-->
    <slot>肯定</slot>
</button>
复制代码

如今当我在一个父级组件中使用<cus-button>而且按钮也是“肯定”的时候,就能够不提供任何内容:

<div class="button-list">
    <cus-bottom></cus-bottom>
</div>
复制代码

固然若是咱们父组件里的按钮是“保存”的时候,还能够这样写

<div class="button-list">
    <!--这里的保存 会替换掉子组件的默认内容-->
    <cus-bottom>保存</cus-bottom>
</div>
复制代码

最终渲染:

<div class="button-list">
    <button class="custom-button">
        保存
    </button>
</div>
复制代码

具名插槽

具备名字的插槽。为何要有这个东西呢?咱们在上面的例子中slot只有一个,因此父组件传过来的内容都被子组件惟一的slot接收了。可是不少时候咱们须要有多个slot来分别接收父组件传过来的 '多份' 内容。

这里咱们使用官网的例子,带有以下内容的 <base-layout> 组件:

<div class="container">
  <header>
    <!-- 咱们但愿把头部内容放在这里 -->
  </header>
  <main>
    <!-- 咱们但愿把主要内容放在这里 -->
  </main>
  <footer>
    <!-- 咱们但愿把页脚放在这里 -->
  </footer>
</div>
复制代码

如今我须要三个slot(插槽),来接收父组件传过来的三分内容。对于这样的状况,slot元素有一个特殊的特性:name。这个特性能够用来定义额外切独立的插槽:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
复制代码

中间一个不带 name 的 slot的插槽 会带有隐含的名字“default”。至关于<slot name="default"></slot>。也就是说如今子组件里有三个名为:header default footer的插槽。

那么如今父组件使用 <base-layout>组件的时候须要传三分内容(都不是必须的),'header','default','footer'。这个时候就须要有个标识告诉子组件三分内容跟三个插槽如何对应上。

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

<base-layout>
  <template v-slot:header>
    <h1>这是header插槽的内容</h1>
  </template>

  <p>这里是default插槽的内容</p>
  <p>这里也是default插槽的内容</p>

  <template v-slot:footer>
    <p>这是footer插槽的内容</p>
  </template>
</base-layout>
复制代码

v-slot :后面就是子组件对应的slotname。由于子组件main标签里的slot没有name,默认name = default,因此还能够这样写

<base-layout>
  <template v-slot:header>
    <h1>这是header插槽的内容</h1>
  </template>

  <template v-slot:default>
    <p>这里是default插槽的内容</p>
    <p>这里也是default插槽的内容</p>
  </template>

  <template v-slot:footer>
    <p>这是footer插槽的内容</p>
  </template>
</base-layout>
复制代码

最终渲染:

<div class="container">
  <header>
    <h1>这是header插槽的内容</h1>
  </header>
  <main>
    <p>这里是default插槽的内容</p>
    <p>这里也是default插槽的内容</p>
  </main>
  <footer>
    <p>这是footer插槽的内容</p>
  </footer>
</div>

复制代码

具名插槽的缩写

v-onv-bind 同样,v-slot 也有缩写,即把参数以前的全部内容 (v-slot:) 替换为字符 #。例如 v-slot:header 能够被重写为 #header

<base-layout>
    <template #header>
        <h1>这是header插槽的内容</h1>
    </template>
    
    <template #default>
        <p>这里是default插槽的内容</p>
        <p>这里也是default插槽的内容</p>
    </template>
    
    <template #footer>
        <p>这是footer插槽的内容</p>
    </template>
</base-layout>
复制代码

做用域插槽

这里讲做用域插槽我以为叫插槽传值更贴切。

例如,设想一个带有以下模板的 <current-user> 组件:

<template>
    <span>
        <slot>{{ userInfo.name }}</slot>
    </span>
</template>

<script>
export default{
    data(){
        return{
            userInfo:{
                name:'erdong',
                sex:'boy',
                age:'26'
            }
        }
    }
}
</script>
复制代码

包括当前用户的全部信息。插槽的默认内容是name。当咱们使用 <current-user> 组件时:

父组件:

<current-user></current-user>
复制代码

最终渲染:

<span>
    erdong
</span>
复制代码

可是我想让父组件里显示sex该怎么办呢?很简单,改变子组件的插槽的默认内容:

<span>
    <slot>{{ userInfo.sex }}</slot>
</span>
复制代码

可是这样达不到咱们封装组件的特性:可复用性。

若是说父组件能拿到子组件里的infoData的值,那咱们就能够这样写:

<!--父组件-->
<current-user>
    {{infoData.sex}}
</current-user>
复制代码

这样就能覆盖子组件里的默认内容。可是咱们在上面提到了 编译做用域 父组件是取不到子组件的变量的。

想让父组件取到infoData该怎么办呢?

这个时候咱们须要更改子组件:

<!--子组件-->
<span>
    <slot v-bind:userInfo="userInfo" name='user'>{{ userInfo.sex }}</slot>
</span>
复制代码

父组件:

<current-user>
    <template v-slot:user="infoData">
        {{infoData.userInfo.sex}}
    </template>
</current-user>
复制代码

最终渲染:

<span>
    boy
</span>
复制代码

子组件v-bind:userInfo="userInfo" name='user' 第一个userInfo是传给父组件的变量名称,第二个userInfo是传给父组件的值。name就是该slot的名称

父组件v-slot:user="infoData" user就是对应子组件的slotname,infoData就是接收该slot传过来的值的集合的变量名称。为何叫集合呢?由于子组件一个slot能够传多个值:

<!--子组件-->
<template>
   <span>
       <slot v-bind:userInfo="userInfo" v-bind:address='address' name='user'>{{ userInfo.name }}</slot>
   </span>
</template>

<script>
export default{
   data(){
       return{
           userInfo:{
               name:'erdong',
               sex:'boy',
               age:'26'
           },
           address:{
               city:'上海市',
           }
       }
   }
}
</script>
复制代码
<!--父组件-->
<current-user>
    <template v-slot:user="infoData">
        {{infoData.userInfo.sex}}{{infoData.address.city}}
    </template>
</current-user>
复制代码

最终渲染:

<span>
    boy上海市
</span>
复制代码

解构插槽 Prop

<!--父组件-->
<current-user>
    <template v-slot:user="infoData">
        {{infoData.userInfo.sex}}{{infoData.address.city}}
    </template>
</current-user>
复制代码

上面提到了 infoData是子组件名为user的插槽传过来的值得集合。 即:

infoData = {
    userInfo:{
        name:'erdong',
        sex:'boy',
        age:'26'
    },
    address:{
        city:'上海市',
    }
}
复制代码

因此咱们能够用ES6的语法来解构它:

<!--父组件-->
<current-user>
    <template v-slot:user="{userInfo,address}">
        {{userInfo.sex}}{{address.city}}
    </template>
</current-user>
复制代码

总结

到这里咱们基本上就把Vueslot(插槽)使用过了一遍,还有一些没有提到,可是若是这些你所有学会的话,能够说你已经掌握了slot的使用。若是报错请查看你的Vue.js版本是不是2.6.x。

课后做业

在上面咱们用封装button的例子来说slot的基本用法。有兴趣的同窗能够完善一下。封装一个相似于element UIbutton组件。能够留着本身项目使用。

相关文章
相关标签/搜索