我们在做项目的过程中经常会封装一些公用的组件,通过props
传值来展示不同的内容,就比如说下面的一个简单的NavBar
组件,可以通过传入不同的title
展示不同的标题。
NavBar组件
<!-- NavBar -->
<template>
<div class='navbar'>
<div class="left">
<span><</span>
<span>返回</span>
</div>
<div class="center">
{{ title }}
</div>
<div class="right">
右侧
</div>
</div>
</template>
父组件
<template>
<div class='home'>
<NavBar title="标题" />
</div>
</template>
但是如果需求是标题处展示icon或者icon加文字呢?这个时候就要通过slot插槽来解决了。
一、匿名插槽
匿名插槽只要在NavBar
组件需要自定义的地方通过<slot></slot>
进行占位,然后在父组件<NavBar></NavBar>
标签之间通过<template></template>
或者直接写要往插槽中填充的内容。
如果NavBar
组件中没有使用slot
标签,下面的img
和span
标签就不会渲染出来。
NavBar组件
<!-- NavBar -->
<template>
<div class='navbar'>
<div class="left">
<span><</span>
<span>返回</span>
</div>
<div class="center">
<slot></slot>
</div>
<div class="right">
右侧
</div>
</div>
</template>
父组件
<template>
<div class='home'>
<NavBar>
<img class="icon" src="../../assets/image/search.png" alt="">
<span>搜索</span>
</NavBar>
</div>
</template>
二、具名插槽
有时在一个组件中包含多个插槽出口,比如在我们的NavBar
组件中,分为左侧、中间、右侧三个地方,则需要三个slot
,此时可以给不同的slot
添加name
属性作为唯一的id,来确定每一处要渲染的内容,而没有添加name
属性的slot
会被隐式命名为default
。
Vue2.6
之前的写法为slot="name"
,Vue2.6
之后的写法为v-slot:name
,也可以简写为#name
。
NavBar组件
<template>
<div class='navbar'>
<div class="left">
<slot name="left"></slot>
</div>
<div class="center">
<slot></slot>
</div>
<div class="right">
<slot name="right"></slot>
</div>
</div>
</template>
父组件
<template>
<div class='home'>
<NavBar>
<template #left>
<span><</span>
<span>返回</span>
</template>
<template #>
<img class="icon" src="../../assets/image/search.png" alt="">
<span>搜索</span>
</template>
<template #right>
右侧
</template>
</NavBar>
</div>
</template>
三、插槽默认内容
在父组件没有提供任何内容的情况下,我们可以为插槽指定默认内容,比如说我们的NavBar
组件,我希望左侧默认就是返回,中间的标题默认就是传过去的文字,而右侧默认就是空的。
NavBar组件
<template>
<div class='navbar'>
<div class="left">
<slot name="left">
<span><</span>
<span>返回</span>
</slot>
</div>
<div class="center">
<slot>{{ title }}</slot>
</div>
<div class="right">
<slot name="right"></slot>
</div>
</div>
</template>
父组件
<template>
<div class='home'>
<NavBar title="标题"></NavBar>
</div>
</template>
此时我们在父组件中没有提供插槽内容,< 返回
和中间传入的title
就会作为默认插槽内容展示出来。
三、作用域插槽
在父组件中使用NavBar
的时候,插槽内容可以访问到父组件的数据作用域,因为变量本来就是在父组件中定义的,如下:
父组件
<template>
<div class='home'>
<NavBar title="标题">
<template #right>
{{ rightValue }}
</template>
</NavBar>
</div>
</template>
<script setup lang='ts'>
import { ref } from 'vue';
import NavBar from './NavBar.vue';
const rightValue = ref('右侧')
</script>
但是插槽中无法访问到子组件的状态,此时我们可以在slot
标签上添加自定义的字段像父组件通过props
给子组件传值一样把数据传递到父组件中。例如:
NavBar组件
<template>
<div class='navbar'>
<div class="left">
<slot name="left">
<span><</span>
<span>返回</span>
</slot>
</div>
<div class="center">
<slot>{{ title }}</slot>
</div>
<div class="right">
<slot name="right" :city="city"></slot>
</div>
</div>
</template>
<script name="NavBar" setup lang='ts'>
import { ref } from 'vue';
defineProps<{
title: string
}>()
const city = ref('上海')
</script>
父组件
<template>
<div class='home'>
<NavBar title="标题">
<template #right="data">
{{ data.city }}
</template>
</NavBar>
</div>
</template>
Vue2.6
之前的写法为<template slot="name" slot-scope="data">
,Vue2.6
之后的写法为v-slot:name="data"
,也可以简写为#name="data"
。
四、动态插槽名
v-slot:
也可以使用动态传参,写作v-slot:[name]
或简写为#[name]
。例如:
父组件
<template>
<div class='home'>
<NavBar title="标题">
<template #[slotName]>
我是 {{ slotName }}
</template>
</NavBar>
</div>
</template>
<script setup lang='ts'>
import { ref } from 'vue';
import NavBar from './NavBar.vue';
const slotName = ref('right')
</script>