插槽
插槽可以让一个父组件调用子组件时,给子组件传入Html内容
当我们想封装一个按钮,按钮的内容可以通过prop传递到子组件,但是如果我们想给按钮添加icon图标呢,插槽是个不错的选择
1.默认插槽
通过一个slot标签,我们可以指定一个插槽
<template>
<button>
<slot></slot>
</button>
</template><template>
<MyButton>
<Icon type="search" /> 搜索
</MyButton>
</template>想要在有插槽的子组件中插入html,子组件必须是双标签,写在双标签里的内容都会在子组件中的slot位置显示
slot标签最终不会渲染成一个真实的dom标签,可以理解为一个占位符
上方代码效果如下图所示

2.插槽内容与出口
插槽的内容在父组件中定义,出口在子组件中定义
也就是说,在父组件中,给子组件的双标签内添加html内容,最终会被渲染到子组件中相应的位置
<Child>
Click me! <!-- 插槽内容 -->
</Child><button class="fancy-btn">
<slot></slot> <!-- 插槽出口 -->
</button><slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

最终渲染出的 DOM 是这样:
<button class="fancy-btn">Click me!</button>插槽内容可以是任意合法的模板内容,不局限于文本。例如我们可以传入多个元素,甚至是组件:
<FancyButton>
<span style="color:red">Click me!</span>
<AwesomeIcon name="plus" />
</FancyButton>3.默认内容
在外部没有提供任何内容的情况下,可以为插槽指定默认内容。
只需在<slot>标签中写入需要的内容
<button type="submit">
<slot>
Submit <!-- 默认内容 -->
</slot>
</button>此时父组件中使用该组件
<template>
<MyButton></MyButton>
</template>最后的button如下所示:

4.具名插槽
可以在一个子组件的不同地方指定多个插槽,同时给这些slot标签定义一个name属性用于区分
没有提供 name 的 <slot> 出口会隐式地命名为“default”。
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>在使用的时候使用v-solt:soltName将指定的内容分传给不同的插槽
<BaseLayout>
<template v-slot:header>
<!-- header 插槽的内容放这里 -->
</template>
<template>
<!-- 没指定的内容会被放进默认插槽里 -->
</template>
<template v-slot:footer>
<!-- footer 插槽的内容放这里 -->
</template>
</BaseLayout>v-slot 有对应的简写 #,因此 <template v-slot:header> 可以简写为 <template #header>。其意思就是“将这部分模板片段传入子组件的 header 插槽中”。
<BaseLayout>
<template #header>
<h1>头部标题</h1>
</template>
<template #default>
<p>文章具体内容</p>
</template>
<template #footer>
<p>页尾</p>
</template>
</BaseLayout>当一个组件同时接收默认插槽和具名插槽时,所有位于顶级的非 <template> 节点都被隐式地视为默认插槽的内容。所以上面也可以写成:
<BaseLayout>
<template #header>
<h1>头部标题</h1>
</template>
<!-- 隐式的默认插槽 -->
<p>文章具体内容</p>
<template #footer>
<p>页尾</p>
</template>
</BaseLayout>5.条件插槽
条件插槽可以根据插槽是否存在来渲染某些内容
在下面的示例中,我们定义了一个卡片组件,它拥有三个条件插槽:header、footer 和 default。 当 header、footer 或 default 存在时,我们希望包装它们以提供额外的样式:
<template>
<div class="card">
<div v-if="$slots.header" class="card-header">
<slot name="header" />
</div>
<div v-if="$slots.default" class="card-content">
<slot />
</div>
<div v-if="$slots.footer" class="card-footer">
<slot name="footer" />
</div>
</div>
</template>6.动态插槽名
动态指令参数在 v-slot 上也是有效的,即可以定义下面这样的动态插槽名:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
<!-- 缩写为 -->
<template #[dynamicSlotName]>
...
</template>
</base-layout>
<script>
import {ref} from 'vue'
const dynamicSlotName = ref('name')
</script>7.作用域插槽
作用域插槽可以在父组件中访问到子组件放在插槽上的数据
在子组件中,直接在slot标签上传递属性
<!-- <MyComponent> 的模板 -->
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div>在父组件中使用v-slot指令,指令值的名字可以自定义,指令的值为一个对象,对象中包含子组件在slot标签上传来的数据
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
v-slot="slotProps" 可以类比这里的函数签名,和函数的参数类似,我们也可以在 v-slot 中使用解构:
<MyComponent v-slot="{ text, count }">
{{ text }} {{ count }}
</MyComponent>作用域插槽可以和具名插槽一块使用
<MyComponent>
<template #header="headerProps">
{{ headerProps }}
</template>
</MyComponent>向具名插槽中传入 props:
<slot name="header" message="hello"></slot>注意插槽上的 name 为了定义具名插槽而特别定义的属性,不会作为 props 传递给插槽。因此最终 headerProps 的结果是 { message: 'hello' }。
当作用域插槽和默认插槽使用时,默认插槽必须要显式的使用 <template> 标签,不然插槽传递的数据无法传递过来
<MyComponent>
<!-- 使用显式的默认插槽 -->
<template #default="{ message }">
<p>{{ message }}</p>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</MyComponent><div>
<slot :message="hello"></slot>
<slot name="footer" />
</div>