Vue系列之插槽(slot)详解

Posted 老__L

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue系列之插槽(slot)详解相关的知识,希望对你有一定的参考价值。

文章の目录


Vue 版本:2.6.11,过期的API就不在这里进行赘述了。

1、什么是插槽了

插槽(Slot)是 Vue 提出来的一个概念,正如名字一样,插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制

2、插槽的分类

插槽有三种:默认插槽、具名插槽、作用域插槽。

3、默认插槽的使用

3.1、语法

<slot></slot>

3.2、示例

在子组件中定义了一个默认插槽:

<template>
  <div class="about">
    <h1>This is an Children page</h1>
    <!-- 定义一个默认插槽 -->
    <slot></slot>
  </div>
</template>

在开发中我们经常使用到组件之间的传值,但很多情况涉及到的都是数据属性的传值,现在如果是这种情况:

<template>
  <div class="about">
    <h1>This is an Parent page</h1>
    <children>
      <!-- 一个p标签的dom结构 -->
      <p>子组件标签之间</p>
    </children>
  </div>
</template>

<script>
import Children from './Children.vue'
export default 
  components: 
    Children
  ,
  data () 
    return 
    
  

</script>

如果子组件中没有使用插槽的情况下:

<template>
  <div class="about">
    <h1>This is an Children page</h1>
  </div>
</template>

浏览器中的页面,父组件中的p标签的内容是不会还给子组件的。

这时候,想让父组件定义的p标签传给子组件并显示,可以在子组件中定义一个默认插槽

<template>
  <div class="about">
    <h1>This is an Children page</h1>
    <!-- 定义一个默认插槽 -->
    <slot></slot>
  </div>
</template>


在子组件中,你可以定义多个默认插槽,并在子组件中决定这些默认插槽的位置,父组件要插入的内容,都会被填充到这些默认的插槽中:

<template>
  <div class="about">
    <h1>This is an Children page</h1>
    <!-- 定义一个默认插槽 -->
    <slot></slot>
    <slot></slot>
  </div>
</template>


当然,父组件定义要插入到子组件插槽的内容,并不一定是dom结构类型,也可以是一个组件,也可以是普通的数据结构,只要子组件有定义插槽,就会把内容填充进去。

4、具名插槽的使用

4.1、什么是具名插槽

其实就是在子组件中定义插槽时,给对应的插槽分别起个名字,方便后边插入父组件将根据name来填充对应的内容。

4.2、语法

<slot name="名称"></slot>

4.3、示例

在子组件中,定义两个具名插槽:

<template>
  <div class="about">
    <h1>This is an Children page</h1>
    <!-- 给插槽加了个name属性,就是所谓的具名插槽了 -->
    <slot name="one"></slot>
    <slot name="two"></slot>
  </div>
</template>

父组件中:

为了验证,子组件中的插槽可以填充任何结构的内容,所以我这边专门在one插槽中插入一个组件,而在two插槽就单纯插入一串普通的数据。

<template>
  <div class="about">
    <h1>This is an Parent page</h1>
    <children>
      <template slot="one">
        <p>one插槽</p>
      </template>
      <template  slot="two">
        two插槽
      </template>
    </children>
  </div>
</template>
// 上面这个是 2.6.0 之前的语法,下面这个是  2.6.0 之后的语法。<template>
  <div class="about">
    <h1>This is an Parent page</h1>
    <children>
      <template v-slot:one>
        <p>one插槽</p>
      </template>
      <template v-slot:two>
        two插槽
      </template>
    </children>
  </div>
</template>

<script>
import Children from './Children.vue'
export default 
  components: 
    Children
  ,
  data () 
    return 
    
  

</script>

页面结果:


当然,子组件可以定义多个同名的具名插槽:

<template>
  <div class="about">
    <h1>This is an Children page</h1>
    <!-- 给插槽加了个name属性,就是所谓的具名插槽了 -->
    <slot name="one"></slot>
    <slot name="two"></slot>
    <slot name="two"></slot>
  </div>
</template>

4.4、缩写

把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header。

    <children>
        <template #[dynamicSlotName]="slotProps">
          <h2>slotProps.obj.name</h2>
        </template>
    </children>

然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:

    <children>
        <template #="slotProps">
          <h2>slotProps.obj.name</h2>
        </template>
    </children>

如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:

    <children>
        <template #default="slotProps">
          <h2>slotProps.obj.name</h2>
        </template>
    </children>

5、作用域插槽的使用

5.1、什么是作用域插槽了

作用域插槽就是实现在子组件自行决定自己要显示什么内容

5.2、语法

<slot :自定义的name=data中的属性或对象></slot>

5.3、示例

子组件:

<template>
  <div class="about">
    <h1>This is an Children page</h1>
    <!-- 子组件中,告诉父组件我要实现obj里面的信息 -->
    <slot :obj="obj"></slot>
  </div>
</template>

<script>
export default 
  data () 
    return 
      obj: 
        name: 'children'
      
    
  

</script>

父组件:

这种时候有一种情况需要注意:

如果子组件中只有一个作用域插槽且为默认插槽时,父组件可以使用下边这种简单的写法,当然规范点还是加在template模板标签好点:

<template>
  <div class="about">
    <h1>This is an Parent page</h1>
    <children v-slot="slotProps">
        <p>one插槽slotProps.obj.name</p>
    </children>
  </div>
</template>

<script>
import Children from './Children.vue'
export default 
  components: 
    Children
  ,
  data () 
    return 
    
  

</script>

页面效果:

但是当子组件中有多个作用域插槽时,不建议上边这种简单的写法,因为可能会出现作用域不明确的问题。

子组件有多个作用域插槽时:

<template>
  <div class="about">
    <h1>This is an Children page</h1>
    <slot :obj1="obj1" name="one"></slot>
    <slot :obj2="obj2" name="two"></slot>
  </div>
</template>

<script>
export default 
  data () 
    return 
      obj1: 
        name: 'one slot'
      ,
      obj2: 
        name: 'two slot'
      
    
  

</script>

父组件使用这种规范的写法,可以避免有时作用域不明确问题:

<template>
  <div class="about">
    <h1>This is an Parent page</h1>
    <children>
        <template v-slot:one="slotProps">
          <h2>slotProps.obj1.name</h2>
        </template>
        <template v-slot:two="twoSlotProps">
          <h2>twoSlotProps.obj2.name</h2>
        </template>
    </children>
  </div>
</template>

<script>
import Children from './Children.vue'
export default 
  components: 
    Children
  ,
  data () 
    return 
    
  

</script>

页面效果:

6、动态插槽名

6.1、什么是动态插槽名

动态指令参数也可以用在 v-slot 上,来定义动态的插槽名

6.2、示例

子组件:

<template>
  <div class="about">
    <h1>This is an Children page</h1>
    <slot :obj="obj1" name="one"></slot>
    <slot :obj="obj2" name="two"></slot>
  </div>
</template>

<script>
export default 
  data () 
    return 
      obj1: 
        name: 'one slot'
      ,
      obj2: 
        name: 'two slot'
      
    
  

</script>

父组件:

<template>
  <div class="about">
    <h1>This is an Parent page</h1>
    <children>
        <template v-slot:[dynamicSlotName]="slotProps">
          <h2>slotProps.obj.name</h2>
        </template>
    </children>
  </div>
</template>

<script>
import Children from './Children.vue'
export default 
  components: 
    Children
  ,
  data () 
    return 
      dynamicSlotName: 'one'
    
  

</script>

显示效果:

写在最后

如果你感觉文章不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O???
如果你觉得该文章有一点点用处,可以给作者点个赞;\\\\*^o^*//
如果你想要和作者一起进步,可以微信扫描二维码,关注前端老L~~~///(^v^)\\\\\\~~~
谢谢各位读者们啦(^_^)∠※!!!

Vue3 插槽使用详解

Vue在2.6.0,版本更新了有关插槽的大量内容,具名插槽和作用域插槽引入了一个新的语法v-slot来取代slot和slot-scope这两个已废弃但未移除的属性。


前言-认识slot

< slot>< /slot>一般被写在子组件里,可以被父组件内写的东西"插"满,把它看作是一个帮 [未来要填充的东西] 提前占位的空框,就像那些一个人占了自习室一整排的可恶的家伙.
我想我用程序里的名词来比喻可能比较好理解?slot就像子组件里一个可以传入参数的函数,你可以从父组件里往里传各种标签作为它的参数.

感谢你浪费一分钟生命读完了这段废话,我们开始吧 doge)


先说下父子组件的概念吧,打个比方,如果父组件是App.vue,子组件就可以是components里的各种.vue文件.

一、作用域插槽

或许一个插槽会需要在父组件内不同的地方多次调用,但是却需要以不同的渲染结果出现,又或者插槽填充物需要访问子组件中的数据才能正常工作;

这时候就可以用作用域插槽了,作用域插槽的渲染是在子组件完成,利用这个特点,在子组件的slot上利用v-bind / v-for什么的绑定上填充物需要的各种数据,这样填充完成后在子组件渲染时就可以从子组件拿数据来渲染了.
绑定在 < slot> 上的属性被称为"插槽prop",也就是此处的user属性了

//这是在子组件内;
<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>

而在父级作用域内使用v-slot属性来取代已经被官方废弃的slot-scope属性;

//这是在父组件内;
<current-user>
//current-user:子组件;
  <template v-slot:default="slotProps(可自定义)">
  /* !!我还是推荐你用简写:<template v-slot="slotProps">,
    那个:default看起来很奇怪!! */
  /* 使用template来包裹用于填充slot的html结构,
    非具名插槽不能填名字 */
  /* (估计这slotProps就是子组件的user) */
  
    {{ slotProps.user.firstName }}
    //此处差值表达式明显需要使用子组件内的数据来渲染;
  </template>
</current-user>

作用域插槽会被解析为一个传入了slotProps作参的函数:

function (slotProps) {
  // 插槽内容
}

v-slot 的值实际上可以是任何 [能作为函数的参数来传入] 的东西;

  <template v-slot="{ user }">
  /* 这里给v-solt赋了个对象 */ 
    {{ slotProps.user.firstName }}
  </template>

动态指令参数与插槽

从 2.6.0 开始,可以用方括号括起来的JS表达式作为一个指令(v-bind啥的)的参数,比如在此例中

//随便定义一个变量randomName,这个变量是动态变化的;
<a v-bind:[randomName]="user"> ... </a>

或者:

//对不同的事件绑定处理函数;
<a v-on:[randomEventName]="differentFunction"> ... </a>

在2.6版本新增的内容中,动态指令参数也可以用在 v-slot 上,来定义动态的插槽名:

<base-layout>
  <template v-slot:[动态指令参数]>
  /* 动态指令参数,就像前面的randomEventName */
    ...
  </template>
</base-layout>

二、具名插槽

有name属性的就是具名插槽了,没有的就是匿名插槽(这么说作用域插槽也属于匿名插槽?).

有时我们会需要在子组件某处写一堆的插槽,又需要在父组件各处分别使用这些插槽.某些方面计算机是很笨的,它不会看得出哪个插槽该填什么东西,也不知道你在这里填的东西是要给哪个插槽,这就得给它规定一下,不然…不然这傲娇它就直接不给你填了.

2.6版本之前具名插槽有直接使用name属性来规定目标的方法(废弃,但没移除),但是今天试了下,好像已经不行了(我没有成功…),那这里就只记录官方推的新方法了.


总之只要出现使用多个插槽的情况,那就用具名插槽吧!
使用< template>来包裹某插槽的填充物,在template标签的v-slot属性后面写上对应插槽的name属性值.
先来看看子组件吧:

<template>
  <div class="tab-bar-item">
    <slot name="item-icon"></slot>
    <slot name="item-text"></slot>
  <!-- 插槽slot最终会被父组件里传入的html元素替换,在插槽上写样式类的东西不会生效,可以用div来包裹slot,利用样式继承性来完成样式修改 -->
  </div>
</template>

父组件:

//这是在父组件内
  <tab-bar-item>
  
    <template v-slot:item-icon>
    /* !!2.6新增:可简写为<template #item-icon> !!*/
         /* 这部分填充入插槽:item-icon */
      <img src="#" />
    </template>
  
    <template v-slot:item-text>
        /* !!2.6新增:可简写为<template #item-text>!! */
        /* 这部分填充入插槽:item-text */
      <div>首页</div>
    </template>
    
  </tab-bar-item>

这样所有在template包裹下的填充内容都会被填入对应的插槽,但如果没有被template包裹呢?
那样就相当于写了个这:

  <template v-slot:default>
  /* 隐含的名字“default”(默认) */
    <p>我是内容</p>
  </template>

Vue官方文档:没有被包裹在带有 v-slot 的 中的内容都会被视为默认插槽的内容.

总结

注意 v-slot 只能添加在 上 (只有一种例外情况);
我为什么要把这些玩意儿写在插槽里,这么麻烦为什么不直接写那些标签?
是,我一开始也在问我自己为什么不直接写标签而要写插槽,跟套娃一样.

后来我意识到这些放在插槽里的东西可以让插槽所在区域的内容动态化,"不写死"的思想又一次在这里得到了体现,只不过这次的变量是插槽里的那段html代码;
而且,如果直接向父组件中的子组件里填充html结构,没有slot占位是渲染不出来(无法创建)的(其实在子组件内的slot上写样式也是不行的,slot一旦渲染就会被替换成填充物,自己的样式就跟着一块被替换下去了,啥都看不见,我们得拿个div什么的把slot包起来然后在div上写样式,让样式继承到未来的填充物上.

写了仨小时,回头看了一眼,崩溃了,参考的是2.6的文档.
马上滚去看了v3的文档,幸亏差的不多,改动也大多都在v2.6完成了,这波是老天帮忙.

以上是关于Vue系列之插槽(slot)详解的主要内容,如果未能解决你的问题,请参考以下文章

大前端之vue插槽slot

Vue.js slot插槽

将插槽传递到 Vue.js 中的插槽

Vue--插槽slot

Vue插槽详解 | 什么是插槽?

Vue.js 源码分析—— Slots 是如何实现的