什么是插槽?
插槽就是子组件提供给父组件使用的一个占位符,可以通俗理解为“占坑作用”。有了该占位符,父组件可以在该占位符中填充任何模板,不仅限于传递数据,也可以填充如HTML、组件、template模板、标签等。填充后,内容替换子组件的标签
为什么要使用插槽?
在组件通信中(详情见文章:Vue组件学习、组件通信),我们了解到组件之间如何传递数据。那父组件在复用子组件的时候除了想给子组件传递数据,还想更改子组件的内容和样式时,怎么办呢?
一般来说,父组件是没办法更改子组件的内容和样式的,总结就是“你可以用我但是不可以更改我”。
就像买车一样 不能自己去决定车的配置、外观、性能、尺寸,这些都是厂家自己设计好然后批量生产的 ,大家买下都是一样的。
如果你真的想更改子组件,那就使用插槽。
插槽的作用就是使得父组件可以改造子组件,让父组件提供内容,在子组件中展示
使用场景:
- 使用插槽让用户拓展组件,更好地复用组件和对其做定制化处理。
- 当一个子组件被父组件复用时,或许在不同情况下需要被更改,此时如果去重写组件是 一件不明智的事情,此时通过slot插槽向子组件内部指定位置传递内容,是一个更好的选择。
- 在布局组件、表格列、下拉框、弹框显示内容中常用。
插槽的作用:
让父组件向子组件指定位置插入html结构,也是一种组件间的通信方式。
案例场景:以下是一个组件的复用(一个蓝色的div即是一个组件),该组件在页面中复用了3次,我想对某一个组件实现私人定制,比如,美食组件我只想放一张图片,电影组件中我想放一个视频,如何做?
由上方效果变成下面这样的:
制定子组件Category.vue:
这是没有使用插槽的情况,组件不能实现定制化:
<template>
<div class="category">
<h3>{{title}}h3>
<ul>
<li v-for="(item,index) in listData" :key="index">{{item}}li>
ul>
div>
template>
<script>
export default{
name:"Category",
// 接收父组件传过来的值
props:['listData','title']
}
script>
<style>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
style>
在App.vue中使用Category.vue:
<template>
<div id="app">
<!-- 复用子组件 静态传输标题,动态传输数组-->
<Category title="美食" :listData="foods" />
<Category title="书籍" :listData="books"/>
<Category title="电影" :listData="films"/>
</div>
</template>
<script scope="this api replaced by slot-scope in 2.5.0+">
import Category from './components/Category.vue';
export default {
name: 'App',
components: {
Category,
},
data(){
return{
// 定义数据
foods:['火锅','重庆火锅','小龙虾','牛排'],
books:['语文','数学','英语','化学'],
films:['重庆森林','七龙珠','犬夜叉','心灵捕手'],
}
}
}
</script>
<style>
#app {
display: flex;
justify-content: space-around;
}
</style>
以上复用只能出现第一个页面效果,见下图:
如果不使用作用域插槽,仅在App.vue更改组件中内容,子组件是不会将内容渲染的
在App.vue中更改子组件内容,此时子组件中无插槽!!!:
<Category title="美食" >
<img src="@/assets/hotpot.jpg" alt="">
</Category>
<Category title="书籍" :listData="books"/>
<Category title="电影" :listData="films"/>
子组件无法渲染在父组件中更改的页面结构,如下图:
下面使用默认插槽:
在子组件Category.vue中定义一个默认插槽:
<template>
<div class="category">
<h3>{{title}}h3>
<slot>默认值,当父组件没有出现时,我会出现slot>
div>
template>
<script>
export default{
name:"Category",
// 接收父组件传过来的值
props:['title']
}
script>
<style>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
style>
App.vue:
<template>
<div id="app">
<Category title="美食" >
<img src="@/assets/hotpot.jpg" alt="">
Category>
<Category title="书籍">
<ul>
<li v-for="(item,index) in books" :key="index">{{item}}li>
ul>
Category>
<Category title="电影">
<video src="@/assets/1.mp4" controls>video>
Category>
div>
template>
<script scope="this api replaced by slot-scope in 2.5.0+">
import Category from './components/Category.vue';
export default {
name: 'App',
components: {
Category,
},
data(){
return{
// 定义数据
foods:['火锅','重庆火锅','小龙虾','牛排'],
books:['语文','数学','英语','化学'],
films:['重庆森林','七龙珠','犬夜叉','心灵捕手'],
}
}
}
script>
<style>
#app {
display: flex;
justify-content: space-around;
}
img{
width: 100%;
}
video{
width: 100%;
}
style>
注意:对样式的定义,可以定义在父组件也可以定义在子组件。
区别:
<style>
img{
width: 100%;
}
video{
width: 100%;
}
style>
上诉案例中,提高需求,我想在分割线下添加一个跳转链接(分割线以上的都是默认插槽区域),如何做?
使用默认插槽和使用作用域插槽的区别:
具名插槽就是把父组件想要更改的元素插在子组件的某个位置上,并且只接受对应名字的更改元素,而默认插槽则会把父组件的更改元素全盘接收。
使用vue2.6的新语法时,建议使用template模板包裹元素
Category.vue
<template>
<div class="category">
<h3>{{title}}h3>
<slot>默认值,当父组件没有出现时,我会出现slot>
-------------------------
<slot name="link">slot>
div>
template>
App.vue
<template>
<div id="app">
<Category title="美食" >
<img src="@/assets/hotpot.jpg" alt="">
<template >
<div class="foot"><a href="">更多美食a>div>
template>
Category>
<Category title="书籍">
<ul>
<li v-for="(item,index) in books" :key="index">{{item}}li>
ul>
<div class="foot" slot="link" >
<a href="https://www.taobao.com/" >去淘宝a>
<a href="https://www.taobao.com/" >去京东a>
div>
<br>
Category>
<Category title="电影">
<video src="@/assets/1.mp4" controls>video>
<template v-slot:link>
<div class="foot">
<a href="">经典a>
<a href="">热门a>
<a href="">推荐a>
div>
<h4>欢迎前来观影h4>
template>
Category>
div>
template>
<script scope="this api replaced by slot-scope in 2.5.0+">
import Category from './components/Category.vue';
export default {
name: 'App',
components: {
Category,
},
data(){
return{
// 定义数据
foods:['火锅','重庆火锅','小龙虾','牛排'],
books:['语文','数学','英语','化学'],
films:['重庆森林','七龙珠','犬夜叉','心灵捕手'],
}
}
}
script>
<style >
#app,.foot{
display: flex;
justify-content: space-around;
}
img{
width: 100%;
}
video{
width: 100%;
}
h4{
text-align: center;
}
style>
理解: 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
案例:
父组件:
<Category title="游戏">
<template scope="row">
<ul>
<li v-for="(g, index) in row.games" :key="index">{{ g }}li>
ul>
template>
Category>
<Category title="游戏">
<template scope="row">
<ol>
<li style="color: red" v-for="(g, index) in row.games" :key="index">
{{ g }}
li>
ol>
template>
Category>
<Category title="游戏">
<template scope="row">
<h4 v-for="(g, index) in row.games" :key="index">{{ g }}h4>
template>
Category>
子组件中:
<h3>{{title}}分类h3>
<slot :games='games'>默认内容slot>
<script>
export default {
props: ['title'],
data() {
return {
games: ['红色警戒', '穿越火线', '劲舞团', '超级玛丽']
}
}
}
script>
作用域插槽可以看成数据的方向流动,有子组件传给父组件
注意:作用域插槽的使用一定要使用template模板