携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
插槽
我们可以使用props来为组件传递不同的数据
但是为了让这个组件具备更强的通用性,我们不能将组件中的内容限制为固定的div、span等等这些元素
比如某种情况下我们使用组件,希望组件显示的是一个按钮,某种情况下我们使用组件希望显示的是一张图片
我们应该让使用者可以决定某一块区域到底存放什么内容和元素,这个时候就需要使用到插槽
插槽的使用过程其实是抽取共性、预留不同
我们会将共同的元素、内容依然在组件内进行封装
同时会将不同的元素使用slot作为占位,让外部决定到底显示什么样的元素
在封装组件中,使用特殊的元素<slot>就可以为封装组件开启一个插槽
该插槽插入什么内容取决于父组件如何使用
子组件
<div>
<!--
slot元素就是对应的插槽标签
如果外部传递过来内容的时候,会替换掉slot标签,显示传递过来的内容
如果外部没有传入对应的内容的时候,会使用slot中编写的预设作为slot的默认值
-->
<slot>
<h2>this is default slot</h2>
</slot>
</div>
父组件
<!-- 传递具体插槽内容的组件 -->
<Child>
<button>click me</button>
</Child>
<!-- 没有传递具体插槽内容,即使用插槽默认值的组件 -->
<Child />
具名插槽
如果一个组件中含有多个插槽,那么对应的内容会依次替换对应的插槽
子组件
<template>
<ul class="nav">
<li class="left">
<slot>left</slot>
</li>
<li class="center">
<slot>center</slot>
</li>
<li class="right">
<slot>right</slot>
</li>
</ul>
</template>
父组件
<template>
<Child>
<button>上一个</button>
<div>内容</div>
<button>下一个</button>
</Child>
</template>
最终效果
如果我们希望在具体的插槽中放置不同的内容的时候,可以使用具名插槽
-
具名插槽顾名思义就是给插槽起一个名字,
<slot>元素有一个特殊的 attribute:name -
一个不带 name 的slot,会带有隐含的名字 default
子组件
<template>
<ul class="nav">
<li class="left">
<!-- 通过name属性给slot起一个具体的名称 -->
<!-- 如果不起名称的话,会存在默认名称 即为default -->
<slot name="left">left</slot>
</li>
<li class="center">
<slot name="center">center</slot>
</li>
<li class="right">
<slot name="right">right</slot>
</li>
</ul>
</template>
父组件
<template>
<Child>
<!--
可以在具体内容外部包裹一层v-slot:[name]
以表示具体希望被插入到那个slot中
如果不写,则表示需要将内容插入到默认插槽中
v-slot:[name] 可以简写为 #[name]
-->
<template v-slot:left>
<button>上一个</button>
</template>
<!--
默认插槽的template可以不写
即被简写为<div>内容</div>
-->
<template v-slot:default>
<div>内容</div>
</template>
<template #right>
<button>下一个</button>
</template>
</Child>
</template>
动态插槽名
所谓动态插槽名,即插槽的名称并不是写死的,而是一个变量
我们可以通过 v-slot:[dynamicSlotName]方式动态绑定一个名称
<template>
<template #[slotName]>
<button>
click me
</button>
</template>
</template>
渲染作用域
父级模板里的所有内容都是在父级作用域中编译的
子模板里的所有内容都是在子作用域中编译的
<Child>
<!--
虽然插槽中的内容,最后是被渲染到子组件中的
但是因为存在渲染作用域的关系,
这里的msg只能是父组件中定义的变量
是无法读取到子组件中对应的变量
-->
<div>{{ msg }}</div>
</Child>
作用域插槽
但是有时候我们希望插槽可以访问到子组件中的内容, 这个Vue给我们提供了作用域插槽
父组件
<template>
<div>
<Child>
<!--
所有子组件传递给父组件插槽部分的内容都会被封装为一个对象
所以可以在获取对应值的时候,直接使用解构语法
-->
<template #default="{username, age}">
<p>{{ username }}</p>
<p>{{ age }}</p>
</template>
</Child>
</div>
</template>
<script>
import Child from './components/Child';
export default {
components: { Child }
}
</script>
子组件
<template>
<div>
<!-- 将父组件中需要使用的子组件中的数据传递给插槽 -->
<slot :username="name" :age="age">
default value
</slot>
</div>
</template>
<script>
export default {
data() {
return {
name: 'Klaus',
age: 23
}
}
}
</script>
独占默认插槽
如果我们的插槽是默认插槽default, 那么在使用的时候 v-slot:default="slotProps"可以简写为v-slot="slotProps"
并且如果我们的插槽只有默认插槽时,组件的标签可以被当做插槽的模板来使用,这样,我们就可以将 v-slot 直接用在组件上
<Cpn #default="{ msg }">
<span>{{ msg }}</span>
</Cpn>
但是,如果我们有默认插槽和具名插槽,那么按照完整的template来编写, 只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template> 的语法