【Vue全家桶】细说slot
文章目录
- 【Vue全家桶】细说slot
- 前言
- 一、认识插槽Slot
- 1.1 插槽的基本使用
- 二、插槽的使用
- 2.1 默认内容
- 2.2 剧名插槽
- 2.3 作用域插槽
前言
我们已经了解到组件能够接收任意类型的 JavaScript 值作为 props,但组件要如何接收模板内容呢?在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。
一、认识插槽Slot
在开发中,我们会经常封装一个个可复用的组件:
- 我们会通过props传递给组件一些数据,让组件来进行展示;
- 但是为了让这个组件具备更强的通用性,我们不能将组件中的内容限制为固定的div、span等等这些元素;
- 比如某种情况下我们使用组件,希望组件显示的是一个按钮,某种情况下我们使用组件希望显示的是一张图片;
- 我们应该让使用者可以决定某一块区域到底存放什么内容和元素;
1.1 插槽的基本使用
这个时候我们就可以来定义插槽slot: 、
- 插槽的使用过程其实是抽取共性、预留不同;
- 我们会将共同的元素、内容依然在组件内进行封装;
- 同时会将不同的元素使用slot作为占位,让外部决定到底显示什么样的元素;
举例来说,这里有一个 <FancyButton>
组件,可以像这样使用:
<FancyButton>
Click me! <!-- 插槽内容 -->
</FancyButton>
而 <FancyButton>
的模板是这样的:
<button class="fancy-btn">
<slot></slot> <!-- 插槽出口 -->
</button>
<slot>
元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。
最终渲染出的 DOM 是这样:
<button class="fancy-btn">Click me!</button>
通过使用插槽,
<FancyButton>
组件更加灵活和具有可复用性。现在组件可以用在不同的地方渲染各异的内容,但同时还保证都具有相同的样式。
二、插槽的使用
2.1 默认内容
有时候我们希望在使用插槽时,如果没有插入对应的内容,那么我们需要显示一个默认的内容:
- 当然这个默认的内容只会在没有提供插入的内容时,才会显示;
在外部没有提供任何内容的情况下,可以为插槽指定默认内容。比如有这样一个 <ShowMessage>
组件:
<template>
<div>
<h2>content 开始</h2>
<slot></slot>
<h2>content 结尾</h2>
</div>
</template>
如果我们想在父组件没有提供任何插槽内容时渲染默认内容,只需要将默认内容写在 <slot>
标签之间来作为默认内容:
<template>
<div>
<h2>slot 开始</h2>
<slot>
<h2>默认显示的内容</h2>
</slot>
<h2>slot 结尾</h2>
</div>
</template>
现在,当我们在父组件中使用 <ShowMessage>
且没有提供任何插槽内容时:
<ShowMessage />
将会把默认内容渲染:
但如果我们提供了插槽内容:
<show-message>
<button>登录</button>
</show-message>
那么被显式提供的内容会取代默认内容:
2.2 剧名插槽
当一个组件中含有多个插槽,我们插入多个内容时
- 会发现默认情况下每个插槽都会获取到我们插入的内容来显示
<template>
<div class="nav-bar">
<div class="left">
<slot></slot>
</div>
<div class="center">
<slot></slot>
</div>
<div class="right">
<slot></slot>
</div>
</div>
</template>
<template>
<div>
<nav-bar>
<button>左边元素</button>
<h2>中间标题</h2>
<button>右边元素</button>
</nav-bar>
</div>
</template>
对于这种场景,<slot>
元素可以有一个特殊的 attribute name
,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容:
<div class="nav-bar">
<div class="left">
<slot name="left"></slot>
</div>
<div class="center">
<slot name="center"></slot>
</div>
<div class="right">
<slot name="right"></slot>
</div>
</div>
这类带 name
的插槽被称为具名插槽 (named slots)。没有提供 name
的 <slot>
出口会隐式地命名为“default”。
在父组件中使用时,我们需要一种方式将多个插槽内容传入到各自目标插槽的出口。此时就需要用到具名插槽了:
要为具名插槽传入内容,我们需要使用一个含 v-slot
指令的 <template>
元素,并将目标插槽的名字传给该指令:
<nav-bar>
<template v-slot:left>
<button>左边元素</button>
</template>
<template v-slot:center>
<h2>中间标题</h2>
</template>
<template v-slot:right>
<button>右边元素</button>
</template>
</nav-bar>
补充:
- 我们可以通过 v-slot:[dynamicSlotName]方式动态绑定一个名称;
v-slot
缩写:(v-slot:) 替换为字符 #;
2.3 作用域插槽
在Vue中有渲染作用域的概念:
- 父级模板里的所有内容都是在父级作用域中编译的;
- 子模板里的所有内容都是在子作用域中编译的;
- 插槽的内容无法访问到子组件的状态
然而在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。
-
我们可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes:
-
当需要接收插槽 props 时,默认插槽和具名插槽的使用方式有一些小区别。
- 默认插槽接受 props,通过子组件标签上的
v-slot
指令,直接接收到了一个插槽 props 对象:
<!-- <MyComponent> 的模板 --> <div> <slot :text="greetingMessage" :count="1"></slot> </div>
<MyComponent v-slot="slotProps"> {{ slotProps.text }} {{ slotProps.count }} </MyComponent>
- 具名作用域插槽的工作方式也是类似的,插槽 props 可以作为
v-slot
指令的值被访问到:v-slot:name="slotProps"
。当使用缩写时是这样:
<MyComponent> <template #header="headerProps"> {{ headerProps }} </template> </MyComponent>
- 向具名插槽中传入 props:
<slot name="header" message="hello"></slot>
- 默认插槽接受 props,通过子组件标签上的