Vue.js插槽组件

这里写一下关于Vue.js插槽的内容。之前在学习vue.js的时候就觉得这个插槽非常有意思,非常神奇,在官方文档跟一些教程的学习后,觉得有必要单独记录一下,所以这里准备了这么一篇。

插槽是什么

官方文档是这么解释的:Vue 实现了一套内容分发的 API,这套 API 基于当前的 Web Components 规范草案,将 <slot> 元素作为承载分发内容的出口。

简单的说,插槽是vue.js的一套API,是用于实现内容分发的,使用<slot> 元素做接口。

从我个人的使用理解来说,就是子组件上留了一些口,这些口就是插槽,这些口可以提供给父组件进行自定义的插入内容,包括字符串,html标签,甚至其他组件。

没有使用插槽的情况

一般来说,在没有使用插槽的情况下,或者子组件没有留插槽的情况下,父组件上在组件标签之间码下的所有内容,都会被抛弃。这也是vue.js官方文档中明确提醒的。也就是说,没有插槽,父子组件之间只有通信的操作,父组件并不能控制在子组件内展示什么内容。

插槽的实现

实现插槽不难,理解插槽就好办了。上面说了,我理解的插槽就是在子组件上留一些口,父组件引用子组件时就可以在这些口上插入内容了。因此,先要在子组件上开口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- child-component -->
<div>
<h2>child component</h2>
<p>slot标签就是子组件留的口,下面的内容就是父组件通过插槽传给子组件的</p>
<slot><slot>
</div>

<!-- parent-component -->
<div>
<h1>parent component</h1>
<child-component>
这部分就是传入子组件slot标签的内容
<p>如果没有指定,所有父组件传入的内容都会通过这个插槽进入子组件</p>
<p>甚至下面的组件也可以被传入</p>
<icon-arrow-right></icon--arrow-right>
</child-component>
</div>

这样就可以实现简单的插槽功能了。但是插槽不只是这些。插槽还分具名插槽,默认插槽。

具名插槽&默认插槽

有时候子组件不止设置一个插槽,还会设置多个插槽。而在父组件上,只能在子组件标签之后传入插槽内容。所以需要一个区分的标识。就像子组件上有多个插槽,父组件要往这些插槽上分别放内容,怎么知道哪些内容放到哪个插槽呢?这就用到<slot>元素上的属性name了。

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
<!-- child-component -->
<div>
<h2>child component</h2>
<header>
<slot name="header"><slot>
</header>
<main>
<slot><slot>
</main>
<footer>
<slot name="footer"><slot>
</footer>
</div>

<!-- parent-component -->
<div>
<h1>parent component</h1>
<child-component>

<template slot="header">
具名插槽
</template>

<p>父组件的内容传入子组件插槽时,可以使用template标签,并使用slot标识对应插槽的位置。这样就可以正确的分发到组件的插槽中。</p>
<p>如果插槽未命名,那么父组件传入的为指定分发位置的内容都会统一放在未命名插槽中,这个插槽就是默认插槽。</p>

<p slot="footer">最后,slot也可以用到普通的元素中</p>

</child-component>
</div>

上面的例子中就实现了默认插槽,父组件所有未指定插槽的内容都会分发到默认插槽中。

默认内容&编译作用域

不过有时候父组件不一定会用上子组件的插槽。这种情况下子组件就可以指定默认的内容。如果父组件有传入内容,指定默认的内容就会被替换掉.

1
2
3
4
5
<main>
<slot>
<p>如果父组件没有给这个插槽传入内容,则在页面上能看到这段子组件默认的内容。</p>
<slot>
</main>

如果想在插槽中使用数据,需要注意作用域的问题:插槽中的内容所在的作用域是父组件的作用域,只能访问父组件作用域内的数据,不能访问子组件作用域内的数据。

1
2
3
<template slot="header">
{{ article.title }}
</template>

作用域插槽

虽然插槽所使用的数据只能是父组件的作用域,但是还是有办法使用子组件的数据的,就是作用域插槽。

作用域插槽就是把子组件需要在插槽内使用的数据作为插槽的prop传入,然后父组件在插槽内的元素或组件中使用slot-scope获取数据:

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
<!-- 引用官方文档的例子 -->
<!-- 子组件todo-list -->
<ul>
<li
v-for="todo in todos"
v-bind:key="todo.id"
>
<!-- 我们为每个 todo 准备了一个插槽,-->
<!-- 将 `todo` 对象作为一个插槽的 prop 传入。-->
<slot v-bind:todo="todo">
<!-- 回退的内容 -->
{{ todo.text }}
</slot>
</li>
</ul>

<!-- 父组件中引用子组件部分 -->
<todo-list v-bind:todos="todos">
<!-- 将 `slotProps` 定义为插槽作用域的名字 -->
<template slot-scope="slotProps">
<!-- 为待办项自定义一个模板,-->
<!-- 通过 `slotProps` 定制每个待办项。-->
<span v-if="slotProps.todo.isComplete">✓</span>
{{ slotProps.todo.text }}
</template>
</todo-list>

这里确认一点:slot-scope不仅可以在<template>元素上使用,还可以在插槽内的任何元素或组件上使用。另外,官方文档还说明了,基于ES2015解构语法,上面父组件中<template slot-scope="slotProps">可以写成<template slot-scope="{ todo }">

以上就是关于vue.js插槽的内容。