Appearance
slot 插槽
老版本vue
模板中只能有一个根元素
HTML内容模板(
<template>)元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以在运行时使用JavaScript实例化。
html
<div id="app">
<modal></modal>
</div>
<template id="modal">
<div>
<h1>是否删除</h1>
</div>
</template>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
javascript
let modal = {
template:"#modal"
}
const app = new Vue({
el:'#app',
components:{
modal
},
data:{
}
})
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
我们通常是想把h1的值动态放入,所以就要用到插槽
单个插槽 | 默认插槽 | 匿名插槽
首先是单个插槽,单个插槽是vue的官方叫法,但是其实也可以叫它默认插槽,或者与具名插槽相对,我们可以叫它匿名插槽。因为它不用设置name属性。 单个插槽可以放置在组件的任意位置,但是就像它的名字一样,一个组件中只能有一个该类插槽。相对应的,具名插槽就可以有很多个,只要名字(name属性)不同就可以了。
html
<div id="app">
<modal>
<h1>插入成功</h1>
</modal>
</div>
<template id="modal">
<div>
<slot></slot>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11

当我们看到插入成功的时候,匿名插入就实现了
具名插槽
匿名插槽没有name属性,所以是匿名插槽,那么,插槽加了name属性,就变成了具名插槽。具名插槽可以在一个组件中出现N次,出现在不同的位置。下面的例子,就是一个有两个具名插槽和单个插槽的组件,这三个插槽被父组件用同一套css样式显示了出来,不同的是内容上略有区别。
简单的来说,就是,我们可能遇到一个问题 我们想插入不同的插槽内的内容不一样
在 2.6.0+ 中已弃用
html
<div id="app">
<modal>
<h1>插入成功</h1>
<h2 slot="title">标题</h2>
<h2 slot="content">内容</h2>
</modal>
</div>
<template id="modal">
<div>
<slot name="default"></slot>
<slot name="title"></slot>
<slot name="content"></slot>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我们可以发现没有name的情况下,默认就是default
作用域插槽 | 带数据的插槽
最后,就是我们的作用域插槽。这个稍微难理解一点。官方叫它作用域插槽,实际上,对比前面两种插槽,我们可以叫它带数据的插槽。什么意思呢,就是前面两种,都是在组件的template里面写
在 2.6.0+ 中已弃用
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue作用域插槽</title>
<script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
<div id="app2">
<!-- 组件使用者只需传递users数据即可 -->
<my-stripe-list :items="users" odd-bgcolor="#D3DCE6" even-bgcolor="#E5E9F2">
<!-- props对象接收来自子组件slot的$index参数 -->
<template slot="cont" scope="props">
<span>{{users[props.$index].id}}</span>
<span>{{users[props.$index].name}}</span>
<span>{{users[props.$index].age}}</span>
<!-- 这里可以自定[编辑][删除]按钮的链接和样式 -->
<a :href="'#edit/id/'+users[props.$index].id">编辑</a>
<a :href="'#del/id/'+users[props.$index].id">删除</a>
</template>
</my-stripe-list>
</div>
<script>
Vue.component('my-stripe-list', {
/*slot的$index可以传递到父组件中*/
template: `
<div>
<div v-for="(item, index) in items" style="line-height:2.2;" :style="index % 2 === 0 ? 'background:'+oddBgcolor : 'background:'+evenBgcolor">
<slot name="cont" :$index="index"></slot>
</div>
</div>
`,
props: {
items: Array,
oddBgcolor: String,
evenBgcolor: String
}
});
new Vue({
el: '#app2',
data: {
users: [{
id: 1,
name: '张三',
age: 20
},
{
id: 2,
name: '李四',
age: 22
},
{
id: 3,
name: '王五',
age: 27
},
{
id: 4,
name: '张龙',
age: 27
},
{
id: 5,
name: '赵虎',
age: 27
}
]
}
});
</script>
</body>
</html>
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
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
这种写法,习惯了element-ui的朋友一定就很熟悉了。
总结: 1 . 使用slot可以在自定义组件内插入原生HTML元素,需要搭配使用name和slot属性,否则多个slot可能会返回重复的HTML元素。 2 . 使用slot-scope可以将slot内部的作用域指向该子组件,否则默认作用域指向调用slot的父组件。
新版本的 v-slot
从 vue@2.6.x 开始,Vue 为具名和范围插槽引入了一个全新的语法,即我们今天要讲的主角:
v-slot指令。目的就是想统一slot和scope-slot语法,使代码更加规范和清晰。既然有新的语法上位,很明显,slot和scope-slot也将会在vue@3.0.x中彻底的跟我们说拜拜了。而从vue@2.6.0开始,官方推荐我们使用v-slot来替代后两者。
具名插槽
实例化一个vue
javascript
// 组件
Vue.component('lv-hello', {
template: `
<div>
<slot name="header"></slot>
<h1>我的天呀</h1>
</div>`
})
new Vue({
el: '#app1',
data: {
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
老版本
html
<div id="app1">
<!-- 老版本使用具名插槽 -->
<lv-hello>
<p slot="header">我是头部</p>
</lv-hello>
</div>
1
2
3
4
5
6
2
3
4
5
6
新版本的变化
html
<!-- 新版本使用具名插槽 -->
<lv-hello>
<!-- 注意:这块的 v-slot 指令只能写在 template 标签上面,而不能放置到 p 标签上 -->
<template v-slot:header>
<p>我是头部</p>
</template>
</lv-hello>
</div>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
具名插槽的缩写
将
v-slot:替换成#号
html
<div id="app">
<lv-hello>
<template #header>
<p>我是头部</p>
</template>
<!-- 注意: #号后面必须有参数,否则会报错。即便是默认插槽,也需要写成 #default -->
<template #default>
<p>我是默认插槽</p>
</template>
</lv-hello>
</div>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
作用域插槽
所谓作用域插槽,就是让插槽的内容能够访问子组件中才有的数据。
javascript
Vue.component('lv-hello', {
data: function () {
return {
firstName: '张',
lastName: '三'
}
},
template: `
<div>
<slot name="header" :firstName="firstName" :lastName="lastName"></slot>
<h1>我的天呀</h1>
</div>
`
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
html
<div id="app">
<!-- 老版本使用具名插槽 -->
<lv-hello>
<p slot="header" slot-scope="hh">我是头部 {{ hh.firstName }} {{ hh.lastName }}</p>
</lv-hello>
<!-- 新版本使用具名插槽 -->
<lv-hello>
<!-- 注意:这块的 v-slot 指令只能写在 template 标签上面,而不能放置到 p 标签上 -->
<template v-slot:header="hh">
<p>我是头部 {{ hh.firstName }} {{ hh.lastName }}</p>
</template>
</lv-hello>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
为什么要使用插槽
组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力。
组件的复用性常见情形如在有相似功能的模块中,他们具有类似的UI界面,通过使用组件间的通信机制传递数据,从而达到一套代码渲染不同数据的效果。
然而这种利用组件间通信的机制只能满足在结构上相同,渲染数据不同的情形;假设两个相似的页面,他们只在某一模块(区域)有不同的UI效果(例如,前面所做的列表,发现可以显示不同的ui效果),以上办法就做不到了。可能你会想,使用 v-if 和 v-else来特殊处理这两个功能模块,不就解决了?很优秀,解决了,但不完美。极端一点,假设我们有一百个这种页面,就需要写一百个v-if、v-else-if、v-else来处理?那组件看起来将不再简小精致,维护起来也不容易。
而 插槽 “SLOT”就可以完美解决这个问题
什么情况下使用插槽
顾名思义,插槽即往卡槽中插入一段功能块。还是举刚才的例子。如果有一百个基本相似,只有一个模块功能不同的页面,而我们只想写一个组件。可以将不同的那个模块单独处理成一个卡片,在需要使用的时候将对应的卡片插入到组件中即可实现对应的完整的功能页。而不是在组件中把所有的情形用if-else罗列出来(这里还是体会用户列表的案例)
可能你会想,那我把一个组件分割成一片片的插槽,需要什么拼接什么,岂不是只要一个组件就能完成所有的功能?思路上没错,但是需要明白的是,卡片是在父组件上代替子组件实现的功能,使用插槽无疑是在给父组件页面增加规模,如果全都使用拼装的方式,和不用组件又有什么区别(例如,用户列表案例中需要其他的显示方式,需要在父组件中进行添加)。因此,插槽并不是用的越多越好。
插槽是组件最大化利用的一种手段,而不是替代组件的策略,当然也不能替代组件。如果能在组件中实现的模块,或者只需要使用一次v-else, 或一次v-else-if,v-else就能解决的问题,都建议直接在组件中实现。