vue在2.6.0中,为具名插槽和作用域插槽引入了新的语法(v-slot指令),之前的slot和slot-scope这两个已经被废弃,但未移除文档。
最近搜了搜百度上边关于插槽相关知识点,内容有点老旧,大多都是2.6.0之前版本的slot,有一些新版的要不就是全篇一堆代码,要不就是跟官方文档一模一样!!!
闲来之余,结合官方文档,详细解读下新版插槽slot:
以下我从8个方面介绍插槽的用法:(之前遗漏掉了插槽解构赋值,今天补上!)
目录
1、插槽内容
2、插槽默认值
3、具名插槽
4、作用域插槽
5、v-slot的特殊情况
6、具名插槽简写
7、动态插槽名
8、(补充)插槽的解构赋值
插槽内容:
通俗来讲,slot作用就是替换自定义标签中的内容,官方称(your profile),slot插槽内可以包含任何模板代码。
<div id="app">
    <son-component>
        <p>吕星辰</p>
    </son-component>
</div>
<script>
 var sonComponent = {
    template:`
        <div>
            <slot></slot>
        </div>
    `
 }
</script>上边的代码,当组件渲染的时候,slot将会被替换成组件中的p标签。组件中也可以包含其它组件。
插槽默认值:
slot插槽默认值:指的是当组件中没有内容,默认值才会被渲染:
<div id="app">
   <son-component>
   </son-component>
</div>
<script>
var sonComponent = {
   template:`
    <div>
       <slot>我是插槽的默认值</slot>
    </div>
    `
}
</script>上边代码,子组件sonComponent里边没有任何内容,这时,子组件模板中的slot插槽中的内容会被渲染出来。
(注意:当组件中有内容时,slot插槽中的默认内容会被覆盖。)
具名插槽:
所谓的具名插槽就是有具体名字的插槽,因开头已经说过,2.6.0版本slot特性已经被废弃,这里就不解释旧版本中的slot了,vue中加了一个新指令v-slot,代替原来的slot;
具体用法:
当我们需要多个插槽,来规定组件中不同的内容在不同位置时,可以用具名插槽:
<div id="app">
    <son-component>
        <template v-slot:footer>
            <h2>我是网页底部</h2>
        </template>
        <template v-slot:title>
            <h1>我是网页标题</h1>
        </template>
        <p>我是网页内容</p>
    </son-component>
</div>
<script>
 var sonComponent = {
    template:`
        <div>
            <slot name="title"></slot>
            <slot></slot>
            <slot name="footer"></slot>
        </div>
    `
 }
</script>渲染:

上边代码:子组件sonComponent中有三段代码,这里故意打乱他们的顺序,我想把标题放到网页顶部,内容放到网页中间,底部对应放在网页底部,你只需要在插槽slot上定义特性name,值是自定义的(尽量语义化),与上边template的v-slot绑定的值对应即可。这时你可以定义多个slot插槽,组件中的内容会根据slot顺序依次插入进去,即使模板中的顺序颠倒!任何没有被包裹在带有v-slot指令的template中的内容,都会视为默认插槽内容(通俗点说就是:不被< template v-slot >包裹的内容,都会插入到匿名插槽内);上边组件中p标签没有被template包裹,当组件渲染会被插入到第二个slot匿名插槽中。
******************** 注意:v-slot只能添加在一个template标签上,当然有一种情况除外,下边介绍 **********************
例:
在移动端ui框架vant中,有好多基于具名插槽的例子,例如:下边loading插槽,就是一个具名插槽!

作用域插槽:
看到文档中作用域插槽时,小编看了半天,一直很蒙B(黑人问号脸),后来自己理解+实践才明白怎么回事。。。
其实也很简单嘛,通俗点说就是,在父组件编译的内容,只能访问到父组件的数据,访问不到子组件的数据。
举个例子:
<div id="app">
    <son-component>
        <h1>{{alias}}</h1>
    </son-component>
</div>
<script>
//子组件
  var sonComponent = {
    template:`
        <div>
            <slot>插槽的默认内容</slot>
        </div>`,
    data(){
        return {
            alias:"我是子组件"
        }
    }
  }
//根实例(父组件)
  var vm = new Vue({
    el: "#app",
    data: {
        alias:"我是父组件"
    },
    components:{
        sonComponent
    }
  })
</script>页面渲染:

上边代码:在父组件和子组件中我们分别定义了alias数据,结果渲染的是父组件中的alias数据。。。
1、h1标签的内容访问不到子组件的数据,只能访问父组件数据,原因在于:它是在父级模板中编译的。
在父级模板里的所有内容都是在父级作用域中编译的,子模板里的所有内容都是在子模板作用域中编译的。(这两句话可以认真的读十遍!!!)
2、通常情况下,需求是:让插槽可以访问到子组件中的data数据,
我们可以在slot上边绑定一个自定义特性,值是子组件data对象中的数据:
<div id="app">
    <son-component>
        <template v-slot:title="slotProp">
            <h1>{{slotProp}}</h1>
        </template>
    </son-component>
</div>
<script>
  var sonComponent = {
    template:`
        <div>
            //绑定属性aliasName,alias是子组件data对象中的数据
            <slot name="title" :aliasName="alias">插槽的默认内容</slot>
        </div>
    `,
    data(){
        return {
            alias:"我是子组件"
        }
    }
  }
</script>页面渲染:

上边代码:
父组件(根实例)我就不写了。
我们在slot上边绑定了一个自定义特性aliasName , 值是alias(就是子组件data中的数据),在官方文档中讲到,绑定在slot上的特性被称为插槽 prop。在父级作用域中,我们给template上的v-slot加上一个值,这个值用来定义插槽prop的名字。(官方文档这句话看似简单,但是你看了会很蒙,因为它没有列出一个完整的例子来;我用大白话给它翻一下:首先给指令v-slot加上的这个值(slotProp),实际上就是给插槽prop对象起一个名字而已,它是一个对象(看上边页面渲染);这个对象的属性正是绑定在插槽slot上的自定义特性aliasName,值是:子组件data对象中的数据,看到这,相信你会明白官方的那个例子,为什么会用点的方式来取值了吧!!!)
当插槽提供了多个prop的时候:
eg:
<div id="app">
    <son-component>
        <template v-slot:self="aliasName">
            <h1>{{aliasName}}</h1>
        </template>
    </son-component>
</div>
<script>
 var sonComponent = {
    template:`
        <div>
            <slot name="self" :alias="alias" :age="age">插槽的默认内容</slot>
        </div>
    `,
    data(){
        return {
            alias:"lxc",
            age:20
        }
    }
 }
</script>页面渲染:

上边代码:不难看出,当出现多个prop时,vue会把所有数据放在一个对象里。
v-slot的特殊情况
当只有默认插槽时(只有匿名插槽,没有具名插槽),组件的标签才可以被当做插槽的模板来使用。这样我们可以把v-slot直接用在组件上。
eg:
<div id="app">
    <!-- 只有默认插槽时, 即不带name的<slot>,会带有一个隐含的名字“default”-->
    <son-component v-slot:default="slotProp">
       <h1>{{slotProp.aliasName}}</h1>
    </son-component>
</div>
<script>
 var sonComponent = {
    template:`
        <div>
            <slot :aliasName="alias">插槽的默认内容</slot>
        </div>
    `,
    data(){
        return {
            alias:"我是子组件"
        }
    }
 }
</script>上边的写法,还可以去掉:default
<div id="app">
    <son-component v-slot="slotProp">
       <h1>{{slotProp.aliasName}}</h1>
    </son-component>
</div>如果出现多个插槽,务必为所有的插槽使用完整的基于<template v-slot:xxx>语法。。。
具名插槽的简写
跟v-bind一样,具名插槽的 v-slot : 也可以简写成 #
(也就是把v-slot和后边的冒号去掉)
<div id="app">
    <son-component>
        <template #self="aliasName">
            <h1>{{aliasName}}</h1>
        </template>
    </son-component>
</div>当只有默认值插槽时,如果你希望简写,必须以明确插槽名的方式书写,如下:
<div id="app">
    //去掉v-slot和冒号(匿名插槽默认带有一个default)
    <son-component #default="slotProp">
       <h1>{{slotProp.aliasName}}</h1>
    </son-component>
</div>下边写法是无效的!!!!!!!!!!!!!!!!!!!!!!!!!!!!
<div id="app">
    //去掉v-slot和冒号
    <son-component #="slotProp">
       <h1>{{slotProp.aliasName}}</h1>
    </son-component>
</div>动态插槽名
所谓的动态插槽名就是v-slot:后边的名字可以是动态的;
eg:
<son-component>
    //dynamicName是一个动态插槽名
  <template v-slot:[dynamicName]="aliasName">
     <h1>{{aliasName}}</h1>
  </template>
</son-component>补充:最近看到有朋友留言说关于动态插槽名有点不明白,今天抽出点空在详细说下,首先要明白的是:动态插槽名值是在父组件定义的(子组件定义是取不到的,因为作用域问题,上边已讲过),父组件可以定义不同的值来渲染不同的内容:
举个例子:
<div id="app">
    <son>
        <!-- 下边alias是一个动态值,根据父组件中的不同值,来渲染不同的内容,getName则是对象的结构赋值 -->
        <template v-slot:[alias]={getName}>
            <h1>{{getName}}</h1>
        </template>
    </son>
</div>
<script>
//子组件
    let son = {
        template:`
            <div>
              <slot name="header" :getName="showName_1">header_默认内容</slot><br>
              <slot name="content" :getName="showName_2">content_默认内容</slot><br>
              <slot name="footer" :getName="showName_3">footer_默认内容</slot>                    
            </div>`,
        data(){
            return{
              showName_1:'my name is header',
              showName_2:'my name is content',
              showName_3:'my name is footer',
            }
        }
    }
//父组件
    let vm = new Vue({
        el:"#app",
        data:{
            alias:'header'
        },
        components:{
            son
        }
    })
</script>渲染结果:

上边代码,在父组件中定义alias值为header,它会去子组件中找与之对应的插槽prop名字,渲染到页面;当我们把父组件中alias值修改为content :
··· ···
//父组件
    let vm = new Vue({
        el:"#app",
        data:{
            alias:'content'
        },
        components:{
            son
        }
    })渲染结果如下:

把父组件中alias值修改为footer:
··· ···
//父组件
    let vm = new Vue({
        el:"#app",
        data:{
            alias:'footer'
        },
        components:{
            son
        }
    })渲染结果如下:

以上就是动态插槽名的用法,至于如何用在项目中去,还是要靠自己慢慢探索!!!
(补充!!!)解构插槽prop
想必大家都能猜出来是什么意思,就是解构插槽的prop,prop正是上文所说的,绑定在插槽slot上的特性就是插槽prop,那怎么解构赋值呢?先来看一个小例子:
<div id="app">
  <son-component>
  <template v-slot:slotname="{myAlias}">
     {{myAlias}}
  </template>
  </son-component>
</div>
<script>
let sonComponent = {
  template: `<div>
            <slot name="slotname" :myAlias=alias>默认值</slot>
    </div>`,
  data(){
  return {
     alias:"lxc"
  }
  },
};
</script>上边代码,我们在slot插槽上绑定了一个特性myAlias,上边v-slot的值是采用对象解构赋值的形式,获取对象,这里要解释下,为什么获取的是对象呢?
当插槽slot上边 有prop时(也就是有绑定特性时),数据会被放到一个对象里边,上边是 { myAlias : " lxc " },基于对象解构赋值,属性名要对应,其次要放到一个对象里;所以v-slot : slotname 后边的值 {myAlias} 对应的是 { myAlias : " lxc " } 。。。
当然有多个插槽prop时,解构赋值是最好的选择:
<div id="app">
  <son-component>
    <template v-slot:slotname="{myAlias,age,hegiht}">
    {{myAlias}}-----{{age}}----{{hegiht}}
    </template>
  </son-component>
</div>
<script>
let sonComponent = {
   template: `
      <div>
        <slot 
           name="slotname" 
           :myAlias=alias
           :age=age
           :height=height
           >
              默认值
         </slot>
       </div>`,
  data(){
     return {
      alias:"lxc",
      age:20,
      height:170
    }
    }
}
</script>页面渲染:

                









