欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Vue3 插槽使用详解

程序员文章站 2022-05-17 10:07:57
...

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完成了,这波是老天帮忙.