1、插槽内容官方文档:https://cn.vuejs.org/v2/guide/components-slots.html
2、什么是插槽?
插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot> 标签。
3、插槽作用
插槽就是Vue实现的一套内容分发的API,可以分发内容,<slot></slot> 元素作为承载分发内容的出口,这句话的意思就是,没有插槽的情况下在组件标签内部一些内容是不起任何作用的,当我在组件中声明了slot元素后,在组件元素内写的内容就会跑到它这里了!
1、<slot></slot> 元素有一个特殊的属性:name。这个属性可以用来定义插槽的名称,一个不带 name 的 <slot></slot> 出口会带有隐含的名字“default”,我们称为“默认插槽”。
2、我们来实现一个代码案例:
1)在components中新建Loading.vue组件(这里演示我们用条形效果):
条形效果:
<!-- Loding组件——条形效果-->
<template>
<div class="loading">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</template>
<style scoped>
.loading{
width: 80px;
height: 40px;
margin: 0 auto;
margin-top:100px;
}
.loading span{
display: inline-block;
width: 8px;
height: 100%;
border-radius: 4px;
background: lightgreen;
margin-left: 5px;
-webkit-animation: load 1.04s ease infinite;
}
@-webkit-keyframes load{
0%,100%{
height: 40px;
background: lightgreen;
}
50%{
height: 60px;
margin-top: -20px;
background: lightblue;
}
}
.loading span:nth-child(2){
-webkit-animation-delay:0.13s;
}
.loading span:nth-child(3){
-webkit-animation-delay:0.26s;
}
.loading span:nth-child(4){
-webkit-animation-delay:0.39s;
}
.loading span:nth-child(5){
-webkit-animation-delay:0.52s;
}
</style>
圆形效果:
<!-- Loding组件——圆形效果 -->
<template>
<div class="loadEffect">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</template>
<style scoped>
.loadEffect{
width: 100px;
height: 100px;
position: relative;
margin: 0 auto;
margin-top:100px;
}
.loadEffect span{
display: inline-block;
width: 16px;
height: 16px;
border-radius: 50%;
background: lightgreen;
position: absolute;
-webkit-animation: load 1.04s ease infinite;
}
@-webkit-keyframes load{
0%{
opacity: 1;
}
100%{
opacity: 0.2;
}
}
.loadEffect span:nth-child(1){
left: 0;
top: 50%;
margin-top:-8px;
-webkit-animation-delay:0.13s;
}
.loadEffect span:nth-child(2){
left: 14px;
top: 14px;
-webkit-animation-delay:0.26s;
}
.loadEffect span:nth-child(3){
left: 50%;
top: 0;
margin-left: -8px;
-webkit-animation-delay:0.39s;
}
.loadEffect span:nth-child(4){
top: 14px;
right:14px;
-webkit-animation-delay:0.52s;
}
.loadEffect span:nth-child(5){
right: 0;
top: 50%;
margin-top:-8px;
-webkit-animation-delay:0.65s;
}
.loadEffect span:nth-child(6){
right: 14px;
bottom:14px;
-webkit-animation-delay:0.78s;
}
.loadEffect span:nth-child(7){
bottom: 0;
left: 50%;
margin-left: -8px;
-webkit-animation-delay:0.91s;
}
.loadEffect span:nth-child(8){
bottom: 14px;
left: 14px;
-webkit-animation-delay:1.04s;
}
</style>

2)在components中新建Center.vue组件,用于显示网站中间内容(不过中间内容不确定,可能是各种其他组件,因此我们使用插槽):
<template>
<div class="center">
<!-- 模板中某个位置,无法确定具体内容 -->
<!-- 使用插槽-默认插槽 slot中可以填写默认内容,如果没有组件插入时显示,有插入时则被替换-->
<slot></slot>
</div>
</template>
<style scoped>
.center {
position: fixed;
left: 50%;
top:50%;
transform: translate(-50%,-50%);
}
</style>
3)App.vue:
<template>
<div id="app">
<Center>
<!-- 在Center组件内部使用Laoding组件会加载到Center的默认插槽 -->
<Loading/>
</Center>
</div>
</template>
<script>
import Center from './components/Center.vue'
import Loading1 from './components/Loading.vue'
export default {
name: 'app',
components: {
Center,
Loading
}
}
</script>
<style>
</style>
4)运行至浏览器

5)插槽用的比较多的就是我们上面的居中Center.vue和蒙层,下面我们再来创建个蒙层Modal.vue:
<template>
<div class="modal">
<slot></slot>
</div>
</template>
<style scoped>
.modal{
z-index: 999;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(55,55,55,.5);
height: 100%;
}
</style>
6)实现加载条在蒙层中居中显示,修改App.vue:
<template>
<div id="app">
<Modal>
<Center>
<Loading/>
</Center>
</Modal>
</div>
</template>
<script>
import Center from './components/Center.vue'
import Loading from './components/Loading.vue'
import Modal from './components/Modal.vue'
export default {
name: 'app',
components: {
Center,
Loading,
Modal
}
}
</script>
<style>
</style>
3、修改Center.vue:
<template>
<div class="center">
<!-- 模板中某个位置,无法确定具体内容 -->
<!-- 使用插槽-默认插槽-->
<slot></slot>
<!-- 使用插槽-具名插槽-->
<slot name="Loading2"></slot>
</div>
</template>
<style scoped>
/* 注释掉居中样式,否则两个加载组件会重叠
.center {
position: fixed;
left: 50%;
top:50%;
transform: translate(-50%,-50%);
} */
</style>
1、具名插槽,就是给这个插槽起个名字。
在向具名插槽提供内容的时候,我们可以在一个 template元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称, v-slot一般只能用在template标签上。
template元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的template中的内容都会被视为默认插槽的内容。然而,如果你希望更明确一些,仍然可以在 template 上使用v-slot:default
2、修改Center.vue的template如下:
<template>
<div class="center">
<!-- 这里面的内容不确定,因此我们可以使用插槽
默认插槽,没有指定name,默认名称为default-->
<slot></slot>
<!-- 具名插槽 -->
<slot name="slot1"></slot>
<slot name="slot2"></slot>
<slot name="loading"></slot>
</div>
</template>
3、修改App.vue的template如下:
<template>
<div id="app">
<Center>
<h1>兄弟们,快去填坑,我先填默认坑</h1>
<template v-slot:slot1>
<div>
我是slot1模板,我去填slot1这个坑
</div>
</template>
<template v-slot:slot2>
<div>
我是slot2模板,我去填slot2这个坑
</div>
</template>
<!-- 插槽中填组件,放在template模板中 -->
<template v-slot:loading>
<Loading></Loading>
</template>
</Center>
</div>
</template>
1、在components中新建Header.vue组件:
<template>
<!-- 头部菜单 -->
<div class="header">
<a href="/">首页</a>
<a href="/blog">博客</a>
<a href="/about">关于我们</a>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.header {
width:1000px;
margin:0 auto;
height:80px;
background:black;
display:flex;
justify-content: center;
align-items:center;
}
.header a{
font-size:18px;
text-decoration: none;
color:white;
margin:0 10px;
}
</style>
3、在components中新建Home.vue组件:
<template>
<div>
这里是首页内容
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
4、在components中新建Footer.vue组件:
<template>
<div class="footer">
底部区域
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.footer{
width:1000px;
line-height:80px;
background: gray;
margin:0 auto;
text-align:center;
color:white;
}
</style>
5、在components中新建Index.vue组件:
<template>
<div class="container">
<!-- 具名插槽,名称为header -->
<slot name="header"></slot>
<!-- 默认插槽,默认名称default -->
<slot></slot>
<!-- 具名插槽,名称为footer -->
<slot name="footer"></slot>
</div>
</template>
<style scoped>
.container{
border: 1px solid gray;
margin:30px auto;
width:1000px;
min-height: 200px;
}
</style>
6、修改App.vue
<template>
<div id="app">
<!-- 将对应的名称的组件插入到对应的Index的插槽中 -->
<Index>
<template v-slot:header>
<Header></Header>
</template>
<Home v-slot:default></Home>
<template v-slot:footer>
<Footer></Footer>
</template>
</Index>
</div>
</template>
<script>
// 导入组件
import Index from './components/Index.vue'
import Header from './components/Header.vue'
import Footer from './components/Footer.vue'
import Home from './components/Home.vue'
export default {
name: 'app',
components: {
// 注册组件
Index,
Header,
Footer,
Home,
}
}
</script>
<style>
.container{
border: 1px solid gray;
margin:30px auto;
width:1000px;
min-height: 200px;
}
</style>
有时让插槽内容能够访问子组件中才有的数据是很有用的。例如如下案例想讲插槽中的姓替换为名字:
1)在components中新建CurrentUser.vue组件:
<template>
<div>
<span>
<!-- 插槽内容默认为姓 -->
<slot>{{user.firstName}}</slot>
</span>
</div>
</template>
<script>
export default {
data(){
return {
user:{
firstName:"潘",
lastName:"子夜"
}
}
}
}
</script>
<style scoped>
</style>
2)修改App.vue如下,想将姓换为对应的名字:
<template>
<div id="app">
<CurrentUser>
<!-- 替换插槽内容为名字 -->
{{user.lastName}}
</CurrentUser>
</div>
</template>
<script>
// 导入组件
import CurrentUser from './components/CurrentUser.vue'
export default {
name: 'app',
components: {
// 注册组件
CurrentUser
}
}
</script>
<style>
.container{
border: 1px solid gray;
margin:30px auto;
width:1000px;
min-height: 200px;
}
</style>
3)运行发现,根本无法实现,因为App.vue中根本无法访问子组件CurrentUser中的user数据,报错如下:

4)为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 slot 元素的一个属性绑定上去,修改CurrentUser.vue中插槽代码如下:
<slot :user="user">{{user.firstName}}</slot>
5)绑定在 slot 元素上的 属性被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字,修改App.vue的CurrentUser部分代码如下:
<CurrentUser>
<!-- 替换插槽内容为名字 -->
<template v-slot:default="slotProps">
{{slotProps.user.lastName}}
</template>
</CurrentUser>
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。
6)运行发现,成功修改:









