大家好我是林三心,Vue 实现了一套内容散发的 API,将<slot>元素作为承载散发内容的进口,这是Vue文档上的阐明。具体来说,slot就是能够让你在组件内增加内容的‘空间’,你真的晓得插槽Slot是怎么“插”的吗?我心愿你们能像我一样单纯,老老实实地看这篇文章。

Vue插槽slot的根本应用

单个插槽 | 匿名插槽

//子组件 : (假如名为:child)<template>  <div class= 'child'>        </div></template>//父组件:(援用子组件 child)<template>  <div class= 'app'>     <child>         林三心     </child>  </div></template>
咱们晓得,如果间接在父组件中的<child></child> 中增加内容“林三心”,“林三心”文本是不会在页面上渲染的。那么咱们如何使增加的内容可能显示呢?在子组件内增加slot 即可。
//子组件 : (假如名为:child)<template>  <div class= 'child'>      <slot></slot>  </div></template>

编译作用域 (父组件在子组件<slot></slot>处插入 data)

下面咱们理解了,slot 其实就是可能让咱们在父组件中增加内容到子组件的‘空间’。咱们能够增加父组件内任意的data值,比方这样:
//父组件:(援用子组件 child)<template>  <div class= 'app'>     <child> {{ parent }}</child>  </div></template>new Vue({  el:'.app',  data:{    parent:'父组件'  }})
应用数据的语法齐全没有变,然而,咱们是否间接应用子组件内的数据呢?显然不行!!
// 子组件 : (假如名为:child)<template>  <div class= 'child'>       <slot></slot>  </div></template>new Vue({  el:'child',  data:{    child:'子组件'  }})// 父组件:(援用子组件 child)<template>  <div class= 'app'>     <child> {{ child }}</child>  </div></template>
间接传入子组件内的数据是不能够的。因为:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

后备内容 (子组件<slot></slot>设置默认值)

所谓的后背内容,其实就是slot的默认值,有时我没有在父组件内增加内容,那么 slot就会显示默认值,如:
//子组件 : (假如名为:child)<template>  <div class='child'>      <slot>这就是默认值</slot>  </div></template>

具名插槽 (子组件多个<slot></slot>对应插入内容)

有时候,兴许子组件内的slot不止一个,那么咱们如何在父组件中,准确的在想要的地位,插入对应的内容呢? 给插槽命一个名即可,即增加name属性。
//子组件 : (假如名为:child)<template>  <div class= 'child'>      <slot name='one'> 这就是默认值1</slot>      <slot name='two'> 这就是默认值2 </slot>      <slot name='three'> 这就是默认值3 </slot>  </div></template>
父组件通过,或slot="name"(旧语法)v-slot:name#name(新语法) 的形式增加内容:
//父组件:(援用子组件 child)<template>  <div class= 'app'>     <child>         <template v-slot:"one"> 这是插入到one插槽的内容 </template>        <template v-slot:"two"> 这是插入到two插槽的内容 </template>        <template v-slot:"three"> 这是插入到three插槽的内容 </template>     </child>  </div></template>

作用域插槽 (父组件在子组件<slot></slot>处应用子组件 data)

通过slot 咱们能够在父组件为子组件增加内容,通过给slot命名的形式,咱们能够增加不止一个地位的内容。然而咱们增加的数据都是父组件内的。下面咱们说过不能间接应用子组件内的数据,然而咱们是否有其余的办法,让咱们可能应用子组件的数据呢? 其实咱们也能够应用slot-scope的形式:
//子组件 : (假如名为:child)<template>  <div class= 'child'>      <slot name= 'one' :value1='child1'> 这就是默认值1</slot>    //绑定child1的数据      <slot :value2='child2'> 这就是默认值2 </slot>  //绑定child2的数据,这里我没有命名slot  </div>           </template>new Vue({  el:'child',  data:{    child1:'数据1',    child2:'数据2'  }})//父组件:(援用子组件 child)<template>  <div class='app'>     <child>         <template v-slot:one='slotone'>             {{ slotone.value1 }}    // 通过v-slot的语法 将子组件的value1值赋值给slotone         </template>        <template v-slot:default='slotde'>            {{ slotde.value2 }}  // 同上,因为子组件没有给slot命名,默认值就为default        </template>     </child>  </div></template>

Slot插槽是怎么“插”的(艰深版)

一般插槽

//子组件 : (假如名为:child)<template>  <div class='child'>      我在子组件外面      <slot></slot>      <slot name="one"></slot>  </div></template>//父组件:(援用子组件 child)<template>  <div class= 'app'>     <child>         这是插入到默认插槽的内容 {{parent}}        <template v-slot:"one"> 这是插入到one插槽的内容 {{parent}}</template>     </child>  </div></template>new Vue({  el:'.app',  data:{    parent:'父组件的值'  }})
  1. 父组件先解析,把 child 当做子元素解决,把 插槽当做 child 的子元素解决,并且在父组件作用域内得出了parent变量的值,生成这样的节点:

    {      tag: "div",    children: [{           tag: "child",           children: ['这是插入到默认插槽的内容 父组件的值',                '这是插入到one插槽的内容 父组件的值']  }]}
  2. 子组件解析,slot 作为一个占位符,会被解析成一个函数,大略意思像 解析成上面

    {     tag: "div",     children: [     '我在子组件外面',     _t('default'), // 匿名插槽,默认名称为default     _t('one') // 具名插槽,名称为one ]}
  3. _t函数须要传入插槽名称,默认是default,具名插槽则传入name,这个函数的作用,是把第一步解析失去的插槽节点拿到,而后返回解析后的节点,那么子组件的节点就残缺了,插槽也胜利认了爹——div标签

    {     tag: "div",     children: ['我在子组件外面',              '这是插入到默认插槽的内容 父组件的值',              '这是插入到one插槽的内容 父组件的值']}

作用域插槽

//子组件 : (假如名为:child)<template>  <div class= 'child'>      <slot :value1='child1' :value2='child1'></slot>      <slot name='one' :value1='child2' :value2='child2'></slot>  </div>           </template>new Vue({  el:'child',  data:{    child1: '子数据1',    child2: '子数据2'  }})//父组件:(援用子组件 child)<template>  <div class='app'>     <child>          <template v-slot:default='slotde'>             插入默认 slot 中{{ slotde.value1 }}{{ slotde.value2 }}        </template>        <template v-slot:one='slotone'>             插入one slot 中{{ slotone.value1 }}{{ slotone.value2 }}        </template>     </child>  </div></template>
  1. 过程很简单,这里就艰深点讲了,父组件先解析,遇到作用域插槽,会将此插槽封装成一个函数保留到子元素 child

    {     tag: "div",      children: [{          tag: "child"  scopeSlots:{                  default (data) { // 记住这个data参数                         return ['插入one slot 中插入默认 slot 中' + data.value1 + data.value2]      },      one (data) { // 记住这个data参数                       return ['插入one slot 中' + data.value1 + data.value2]      }  } }]}

2.轮到子组件解析了,这个时候_t函数又退场了,并且子组件将对应的插槽数据包装成一个对象,传进_t函数

{     tag: "div",       children: [     '我在子组件外面',      _t('default',{value1: '子数据1', value2: '子数据1'}),      _t('one',{value1: '子数据2', value2: '子数据2'})          ]  }
接下来就是_t外部执行,包装后的对象被当做data参数传入了scopeSlots中的对应的各个函数,解析成:
{      tag: "div",       children: [      '我在子组件外面',       '插入默认 slot 中 子数据1 子数据1',      '插入one slot 中 子数据2 子数据2'   ]}

$slots

看到这,置信大家曾经分明了大略一个过程(尽管不是很具体),那么又有一个疑难了,这些解析后的节点VNode对象,是存在哪儿呢?你总不能解析进去而后就扔了吧?必定得找个中央存起来而后进行实在dom的渲染,这个中央就是$slots
//子组件 : (假如名为:child)<template>  <div class= 'child'>      <slot></slot>      <slot name='one'></slot>      <slot name='two'></slot>      <slot name='three'></slot>  </div></template>new Vue({  el:'.child',  created () {      console.log(this.$slots) // 看看外面有啥  }})
//父组件:(援用子组件 child)<template>  <div class= 'app'>     <child>         <template> 这是插入到默认插槽的内容 </template>        <template v-slot:"one"> 这是插入到one插槽的内容 </template>        <template v-slot:"two"> 这是插入到two插槽的内容 </template>        <template v-slot:"three"> 这是插入到three插槽的内容 </template>     </child>  </div></template>
console.log的后果:

看到这里大家都懂了,$slots是一个Mapkey是各个插槽的名称(匿名插槽的keydefault),key对应的value都是各个插槽上面的VNode节点,具体VNode对象里都是长什么样,大家能够本人输入看看,外面货色太多,我就不在这展现了。嘿嘿。

结语

我是林三心,一个热心的前端菜鸟程序员。如果你上进,喜爱前端,想学习前端,那咱们能够交朋友,一起摸鱼哈哈,摸鱼群,加我请备注【思否】