小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
关于slot
闲时要有吃紧的心思,忙时要有悠闲的趣味
前言
在实现组件多态方式重用时,我们通常会通过props传递给组件一些数据,让组件来进行展示。但是为了让这个组件具备更强的通用性,我们更希望我们组件中有部分的结构也可以由用户进行自定义操作,而不是只能自定义数据,即预先定义一些占位元素,具体结构由组件调用者自己决定。
为此,Vue.js实现了一个内容分发API——插槽(slot)
正文
一、什么是插槽?
插槽,其实就相当于占位符。它在组件中给你的HTML模板占了一个位置,,让外部决定到底显示什么样的元素和内容。官网其实讲的很详细的--插槽 | Vue.js
-
如果外部传入了插槽的内容,那么就显示外部传入的内容
-
如果外部什么数据和元素都没有传入的时候,就不进行任何的渲染(如果设置了slot的默认值,此时会显示slot的默认值)
插槽又分为 匿名插槽、具名插槽、作用域插槽。
Vue3(其实从2.6开始),我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope
使用场景
通过插槽可以让用户扩展组件,去更好的复用组件和对其做定制化处理。如果父组件在使用一个复用组件的时候,获取这个组件在不同的地方都有一定的差别。可以通过slot插槽向组件的内部的指定位置传递一些内容,完成这个组件在不同场合的使用。
比如对话框:有的兑换框需要按钮有的不需要,此时就可以用slot插槽占位,如果给它传按钮就显示按钮,如果不传就没有。
二、匿名插槽
即默认插槽,用slot标签来确定占位的位置,标签里面可以放默认的内容,当父组件中没有像指定的插槽插入内容,那么就使用默认的内容。
示例:
- 在子组件
DefaultSlots.vue中使用匿名插槽<slot>
<div class="my_content">
<p>我是默认插槽</p>
<!-- 这里使用插槽作为占位符 -->
<!-- 由外部调用者决定具体存放什么内容 -->
<slot>
<!-- 这个默认的内容只会在没有提供插入的内容时,才会显示 -->
<p>这里是默认内容</p>
</slot>
</div>
- 在父组件
DefaultSlots.vue中引入、注册并使用子组件DefaultSlots,并且向插槽传入相应内容
<template>
<h1>SlotDemo</h1>
<div>
<div>我是父组件</div>
<!-- 使用匿名插槽 -->
<DefaultSlots>
<p>父组件传入的内容</p>
</DefaultSlots>
</div>
</template>
<script>
import DefaultSlots from '@/components/DefaultSlots.vue';
export default {
name: 'SlotDemo',
components: {
DefaultSlots,
},
},
};
</script>
渲染后的结果如下:
三、具名插槽
有时我们需要多个插槽,并且需要对每个插槽传不同的内容。此时还是用默认插槽的话,类似这样:
<div class="my_content">
<p>我是默认插槽</p>
<slot />
<slot />
<slot />
</div>
这只能会使我们父元素传的内容重复展示,就像下面一样:
这个时候具名插糟就派上用场了。
<slot> 元素有一个特殊的 attribute: name 。我们给每个插槽起 name ,就可以让他们与要插入的内容一一对应。
注
- 一个不带 name 的
slot>出口会带有隐含的名字default。
在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称。
示例:
- 在子组件
NamedSlots.vue中使用具名插槽 ,给每个<slot>都取一个name,告诉他们你的幸福……,当然,name也可以是动态的,有父组件来指定。
<template>
<div class="my_content">
<p>我是具名插槽</p>
<!-- 具名插槽顾名思义就是给插槽起一个名字-->
<slot name="header">
<p>这里是默认header</p>
</slot>
<slot name="main">
<p> 这里是默认main</p>
</slot>
<!-- 动态指定插槽名 -->
<slot :name="slotName">
<p>
这里是默认footer
</p>
</slot>
</div>
</template>
<script>
export default {
name: 'NamedSlots',
props: {
slotName: String,
},
};
</script>
- 在父组件
DefaultSlots.vue中引入、注册并使用子组件NamedSlots,并且向插槽传入相应内容,并使用v-slot:来指定插槽名称,告诉该内容是属于那个插槽的,当然,我们也可以使用缩写#来替代v-slot:。
<!-- 使用具名插槽 -->
<NamedSlots :slotName="slotName">
<!-- 没有名字的内容不会被具名插槽匹配 -->
<p>父组件传入的内容</p>
<template v-slot:header>
<p>父组件传入的Header</p>
</template>
<!-- 跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 # -->
<template #main>
<p>父组件传入的Main</p>
</template>
<!-- v-slot:[dynamicSlotName]方式动态绑定一个名称 -->
<template #[slotName]>
<p>父组件传入的Footer</p>
</template>
</NamedSlots>
渲染后的结果如下:
四、作用域插槽
编译作用域
不知道你有没有想过:当你想在一个插槽中使用数据时,会绑定到父组件的数据,还是绑定到子组件的数据呢?
假定模板为:
<child-component>
{{message}}
</child-component
答案是父组件,因为他们的编译作用域不一样
组件作用域简单的说是:
- 父组件模板的内容在父组件作用域内编译;
- 子组件模板的内容在子组件作用域内编译。
那么我们想让插槽内容能够访问子组件中才有的数据,应该怎么办呢?此时 作用域插槽 就排上用场了。
这类插槽可以让父组件使用子组件的数据,只需子组件在插槽中用 v-bind (或者 : )绑定自身的数据,然后父组件在使用组件时就可以用 v-slot="slotProps" 接收,就能直接使用了。
示例:
- 子组件在插槽中用
v-bind(或者:)绑定自身的数据
<template>
<div class="my_content">
<h3>我是作用域插槽</h3>
<!--
将子组件的数据 msg 作为 <slot> 元素的一个属性绑定上去。
绑定在 <slot> 元素上的属性被称为 插槽 prop。
插槽 prop可以传递插槽调用者以供使用。
-->
<slot :msg="msg" />
</div>
</template>
<script>
export default {
name: 'ScopedSlots',
data() {
return {
msg: 'Msg in Child',
};
},
};
</script>
- 在父组件 用
v-slot="slotProps"接收插槽 prop,就能直接使用了。 注意:这里的slotProps可以自定义。
<template>
<h1>SlotDemo</h1>
<div>
<div>我是父组件</div>
<!--作用域插槽 -->
<ScopedSlots>
<!--
// 所有传递给插槽调用者的数据(插槽 prop)都会以键值对的形式存放到slotScope中
// 该案例中 slotScope 的值为 {msg: 'Msg in Child'},因此我们也可以直接解构解构插槽 Prop,如 v-slot="{ msg }"
// 注意: slotScope只是一个变量的名称,可以是任意合法的JS变量名
-->
<template v-slot="slotScope">
<p>{{ slotScope.msg }}</p>
</template>
</ScopedSlots>
</div>
</template>
<script>
import ScopedSlots from '@/components/ScopedSlots.vue';
export default {
name: 'SlotDemo',
components: {
ScopedSlots,
},
data() {
return {
msg: 'Msg in father',
};
},
};
</script>
渲染后的结果如下:
总结
与诸君共勉,为自己加油!
参考文档
后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址
文档协议
db 的文档库 由 db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。