什么是插槽
- 插槽(Slot)是Vue提出来的一个概念,正如名字一样,插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。
- 插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制
默认插槽-1
<!--子组件-->
<template>
<div>
<slot></slot>
<div>slot固有内容</div>
</div>
</template>
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot></named-slot>
</div>
</template>
当父组件不在插槽中写入任何数据时,会只显示父组件和子组件的默认内容:
默认插槽-2
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
我是父组件中的匿名插槽内容
<div style="color: red">我是父组件中的匿名插槽内容</div>
<el-button>插槽按钮</el-button>
</named-slot>
</div>
</template>
当需要在子组件slot位置显示内容时,可以在父组件引入的子组件中写入想要现实的内容(可以直接写入内容,或者使用标签,或者使用其他组件)
默认插槽-3
也可以为插槽设置具体的默认内容,它只会在没有提供内容的时候被渲染
<!--子组件-->
<template>
<div>
<slot>子组件插槽默认内容</slot>
<div>slot固有内容</div>
</div>
</template>
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot></named-slot>
</div>
</template>
父组件没有提供内容时,显示子组件的插槽默认内容:
默认插槽-4
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
我是父组件中的插槽内容,我会替换子组件slot中的默认内容
</named-slot>
</div>
</template>
父组件提供内容时,子组件的默认内容被父组件提供的内容替换:
具名插槽-1
有时候我们需要多个插槽,以把不同的内容显示在需要的地方
一个不带 name
的 <slot>
出口会带有隐含的名字“default”。
<!--子组件-->
<template>
<div>
<slot name="header"></slot>
<div>slot固有内容</div>
<slot></slot>
<div>slot固有内容</div>
<slot name="footer"></slot>
</div>
</template>
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<div>默认插槽1</div>
<div slot="footer">底部插槽</div>
<div slot="header">顶部插槽</div>
<div>默认插槽2</div>
<div slot="default">默认插槽3</div>
</named-slot>
</div>
</template>
上面的代码显示内容如下:
具名插槽-2
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称
注意 v-slot
只能添加在 <template>
上 (只有一种例外情况)
所以父组件的写法也可以是这样:
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<template>默认插槽1</template>
<template v-slot:footer>底部插槽</template>
<template v-slot:header>顶部插槽</template>
<template>默认插槽2</template>
</named-slot>
</div>
</template>
显示如下:
或者写成这样:
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<template v-slot:footer>底部插槽</template>
<template v-slot:header>顶部插槽</template>
<template v-slot:default>默认插槽3</template>
</named-slot>
</div>
</template>
显示如下:
当使用
v-slot
时,只会显示最后一个带有v-slot
的<template>
元素中的内容
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<template v-slot:default>默认插槽1</template>
<template v-slot:footer>底部插槽</template>
<template v-slot:header>顶部插槽</template>
<template v-slot:default>默认插槽2</template>
<template>默认插槽3</template>
</named-slot>
</div>
</template>
以上代码显示:
具名插槽的缩写
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:)
替换为字符 #
。例如 v-slot:header
可以被重写为 #header:
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<template #default>默认插槽1</template>
<template #footer>底部插槽</template>
<template #header>顶部插槽</template>
</named-slot>
</div>
</template>
上面代码显示为:
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
(官网写会触发警告,我的页面直接报错了......)
<!-- 这样会触发一个警告 -->
<named-slot>
<template #>顶部插槽</template>
</named-slot>
所以如果想使用缩写的话,还是老老实实地写清楚插槽名称吧
编译作用域
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
当你想在一个插槽中使用数据时,例如下例,想在父组件中使用子组件的数据是访问不到的:
<!--子组件-->
<template>
<div>
<slot></slot>
<div>slot固有内容: {{ child }}</div>
</div>
</template>
<script>
export default {
name: "mySlot",
data() {
return {
child: "子组件属性"
}
}
}
</script>
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<div>我是父组件中的匿名插槽内容: {{ child }}</div>
</named-slot>
</div>
</template>
作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的。
<!--子组件-->
<template>
<div>
<slot :data="user"></slot>
<div>slot固有内容: {{ user.name }}</div>
</div>
</template>
<script>
export default {
name: "mySlot",
data() {
return {
user: {
name: "明妃",
age: "18"
}
}
}
}
</script>
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<template slot-scope="slotProps">我是父组件中的匿名插槽内容: {{ slotProps.data.name }}</template>
</named-slot>
</div>
</template>
其中子组件中:data="user"
用来把子组件中的user
绑定到data
上
父组件中slotProps
用来接收子组件传过来的数据,slotProps.data
即是子组件传递过来的user
上面代码显示如下:
自2.6.0之后,
slot-scope
已弃用,所以也可以下面的写法:
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<template v-slot:default="slotProps">我是父组件中的匿名插槽内容: {{ slotProps.data.name }}</template>
</named-slot>
</div>
</template>
独占默认插槽的缩写语法
在上述情况下,当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot
直接用在组件上:
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot v-slot:default="slotProps">
我是父组件中的匿名插槽内容: {{ slotProps.data.name }}
</named-slot>
</div>
</template>
这种写法还可以更简单。就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot
被假定对应默认插槽:
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot v-slot="slotProps">
我是父组件中的匿名插槽内容: {{ slotProps.data.name }}
</named-slot>
</div>
</template>
这两种写法都可以获得跟上面一样的效果:
注意默认插槽的缩写语法不能与具名插槽混用,因为他会导致作用域不明确
只要出现多个插槽,请始终为所有的插槽使用完整的基于
<template>
的语法
动态插槽名
动态指令参数也可以用在v-slot
上,来定义动态的插槽名:
<!--子组件-->
<template>
<div>
<slot name="header"></slot>
<div>slot固有内容</div>
<slot></slot>
<div>slot固有内容</div>
<slot name="footer"></slot>
</div>
</template>
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<template v-slot:[slotNameC]>看看插槽位置</template>
</named-slot>
</div>
</template>
data() {
return{
slotNameC: "footer", // footer,header,default
}
},
给slotNameC
赋不同的值,插槽内容会显示在对应位置
但是下面的写法会报错:
<!--父组件-->
<template>
<div class="content">
<div>我是父组件</div>
<named-slot>
<template v-slot:[slotNameA]>默认插槽</template>
<template v-slot:[slotNameB]>顶部插槽</template>
<template v-slot:[slotNameC]>底部插槽</template>
</named-slot>
</div>
</template>
data() {
return{
slotNameA: "default",
slotNameB: "header",
slotNameC: "footer", // footer,header,default
}
},
只有注释掉上面两行才可以正常显示,注释掉第三行不能正常显示(只剩第一行或第二行都不行),目前还不知道原因,希望知道原因的小伙伴告诉一下