2-16-插槽

什么是插槽

插槽(Slot)是vue为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。

示例代码

Left.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<template>

<div class="left-container">

<h3>Left组件</h3>

<hr>

<slot>

<h6>这是slot的默认内容,如果有替换的内容,这一条会被替换掉</h6>

</slot>

</div>

</template>

<script>

export default {}

</script>

<style lang="less">

.left-container {

padding: 0 20px 20px;

background-color: orange;

min-height: 250px;

flex: 1;

}

</style>

**Main.vue**

<template>

<div class="app-container">

<h1>App 根组件</h1>

<hr/>

<div class="box">

<Left>

<p>这是在Left组件的内容区域,声明的p标签</p> //
如果想在Left中插入p标签,就需要在Left中插入Slot

</Left>

</div>

</div>

</template>

name属性

vue官方规定:每一个slot插槽,都要有一个name属性。

如果省略这个name属性,那么它的值就是defalut。

1
<slot name="default"></slot>

默认情况下,插入的内容会自动替代值为defalut的插槽。

1
2
3
4
5
6
<Left>

<p>这是在Left组件的内容区域,声明的p标签</p> //
如果想在Left中插入p标签,就需要在Left中插入Slot

</Left>

中的p会自动替换掉

具名插槽v-slot

如果想要指定特定标签渲染特定的slot,就要用到v-slot属性。

但是v-slot不能直接放到标签上

1
<p v-slot:default>这是在Left组件的内容区域,声明的p标签</p>

这样会报错:

报错说明v-slot只能用在template之中。

像下面这样:

1
2
3
4
5
<template v-slot:default>

<p>这是在Left组件的内容区域,声明的p标签</p>

</template>

其中v-slot可以简写成#,就像下面这样:

1
2
3
4
5
<template #defalut>

<p>这是在Left组件的内容区域,声明的p标签</p>

</template>

具名插槽实例

常用的组件库Vant就常需要插槽来定义自定义内容:

另外,我们也可以定一个Article.vue,在Article.vue中加入各种slot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<template>

<div class="article-container">

<div class="header-box">

<slot name="header">

<div>默认头部</div>

</slot>

</div>

<div class="content-box">

<slot name="content">

<div>默认内容</div>

</slot>

</div>

<div class="footer-box">

<slot name="footer">

<div>默认尾部</div>

</slot>

</div>

</div>

</template>

<script>

export default {

name: "Article"

}

</script>

<style lang="less" scoped>

.article-container {

> div {

min-height: 150px;

}

.header-box {

background-color: pink;

}

.content-box {

background-color: lightblue;

}

.footer-box {

background-color: lightsalmon;

}

}

</style>

然后在App.vue中实现slot

1
2
3
4
5
6
7
8
9
<template>

<div id="app">

<Article>

<template #header>

<div>

这是头部

1
2
3
4
5
6
7
</div>

</template>

<template #content>

<div>

这是内容

1
2
3
4
5
6
7
</div>

</template>

<template #footer>

<div>

这是尾部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
</div>

</template>

</Article>

</div>

</template>

<script>

import Article from '@/components/Article.vue'

export default {

name: 'App',

components: {

Article

}

}

</script>

<style lang="less">

</style>

作用域插槽

在封装组件时,为预留的提供属性对应的值,这种用法,就叫做”作用域插槽”。

1
<slot name="content" msg="hello vue.js"></slot>

需要使用时,就在主vue中赋值为scope,这个scope中就包含了上面定义的msg

1
2
3
4
5
<template #content="scope">

<div>{{ scope.msg }}</div>

</template>

解构赋值

我们可以在data中定义用户的相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>

export default {

name: "Article",

data() {

return {

userinfo: {

name: "zs",

age: 20

}

}

}

}

</script>

然后在插槽中定义一个属性user,将数据动态绑定到该属性上面

1
2
<slot name="content" msg="hello vue.js"
:user="userinfo"></slot>

最后打印scope

1
2
3
4
5
6
7
8
9
<template #content="scope">

<div>

<p> {{ scope }} </p>

</div>

</template>

就会发现相关数据:

当然也可以直接访问到scope内的值

1
<p> {{ scope.user }} </p>

或者将scope直接替换成解构的值

1
2
3
4
5
6
7
8
9
<template #content="{ msg, user }">

<div>

<p>{{ user.name }}</p>

</div>

</template>

改造购物车

本次的主要改造点是把Goods组件中的Counter组件提出来,替换成slot。

首先注释掉eventBus的相关代码,将Counter引入App.vue中:

1
2
3
4
5
6
7
8
9
10
11
import Counter from '@/components/Counter.vue'

export defalut {

components: {

Counter

}

}

在Goods组件中添加slot

1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="goods-info">

<div class="goods-title"></div>

<div class="goods-info-bottom">

<div class="goods-price"></div>

<slot></slot>

</div>

</div>

在Good中渲染Counter

1
2
3
4
5
<Goods ...>

<Counter></Counter>

</Goods>

接着将物品的数量传入到Counter中

1
<Counter :num="item.goods_count"></Counter>

再然后要让Counter组件中的加号和减号修改的数值能够通知到外层的item,就要给它们绑定事件

1
2
3
4
5
6
7
8
9
10
11
add() {

this.$emit("num-change", ++this.count);

}

sub() {

this.$emit("num-change", --this.count);

}

此时在外部传给这个事件一个item,接着用$event接收内部传出的数据,便可以直接修改item中的goods_count

1
2
<Counter :num="item.goods_count" @num-change="getNewNum(item,
$event)"></Counter>

最后实现getNewNum函数即可:

1
2
3
4
5
6
7
8
method: {

getNewNum(item, e) {

item.goods_count = e;

}
}

参考:

Vue2.0-07.插槽 -
了解插槽的基本用法

Vue2.0-08.插槽 -
v-slot指令

Vue2.0-09.插槽 -
v-slot的简写形式以及插槽的后备内容

Vue2.0-10.插槽 -
具名插槽的定义和使用

Vue2.0-11.插槽 -
作用域插槽的基本用法

Vue2.0-12.插槽 -
作用域插槽的解构赋值

Vue2.0-13.插槽 - 基于slot插槽改造购物车案例 -
1

Vue2.0-14.插槽 - 基于slot插槽改造购物车案例 -
2

Vue2.0-15.插槽 - 基于slot插槽改造购物车案例 -
3


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!