一、插槽内容与出口
-
这里有一个
<FancyButton>
组件,可以像这样使用:
-
而
<FancyButton>
的模板是这样的:
-
<slot>
元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。
-
最终渲染出的 DOM 是这样:
-
通过使用插槽,
<FancyButton>
仅负责渲染外层的<button>
(以及相应的样式),而其内部的内容由父组件提供。 -
理解插槽的另一种方式是和下面的 JavaScript 函数作类比,其概念是类似的:
-
通过使用插槽,
<FancyButton>
组件更加灵活和具有可复用性。现在组件可以用在不同的地方渲染各异的内容,但同时还保证都具有相同的样式。 -
Vue 组件的插槽机制是受原生
Web Component <slot>
元素的启发而诞生,同时还做了一些功能拓展。
二、渲染作用域
- 插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。举例来说:
- 这里的两个 {{ message }} 插值表达式渲染的内容都是一样的。
- 插槽内容无法访问子组件的数据。Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。
- 换言之:父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。
三、默认内容
- 如果我们想在父组件没有提供任何插槽内容时在
<button>
内渲染“Submit”,只需要将“Submit”写在<slot>
标签之间来作为默认内容:
四、具名插槽
- 有时在一个组件中包含多个插槽出口是很有用的。举例来说,在一个
<BaseLayout>
组件中,有如下模板:
- 对于这种场景,
<slot>
元素可以有一个特殊的 attribute name,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容:
- 这类带
name
的插槽被称为具名插槽 (named slots)。没有提供 name 的<slot>
出口会隐式地命名为“default”。 - 在父组件中使用
<BaseLayout>
时,我们需要一种方式将多个插槽内容传入到各自目标插槽的出口。此时就需要用到具名插槽了: - 要为具名插槽传入内容,我们需要使用一个含 v-slot 指令的
<template>
元素,并将目标插槽的名字传给该指令:
v-slot
有对应的简写#
,因此<template v-slot:header>
可以简写为<template #header>
。其意思就是“将这部分模板片段传入子组件的 header 插槽中”。
- 使用 JavaScript 函数来类比可能更有助于你来理解具名插槽:
五、动态插槽名
- 动态指令参数在
v-slot
上也是有效的,即可以定义下面这样的动态插槽名:
六、作用域插槽
- 在上面的渲染作用域中我们讨论到,插槽的内容无法访问到子组件的状态。
- 然而在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。
- 我们也确实有办法这么做!可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes:
- 当需要接收插槽 props 时,默认插槽和具名插槽的使用方式有一些小区别。
- 下面我们将先展示默认插槽如何接受 props,通过子组件标签上的
v-slot
指令,直接接收到了一个插槽 props 对象:
- 子组件传入插槽的 props 作为了 v-slot 指令的值,可以在插槽内的表达式中访问。
- 你可以将作用域插槽类比为一个传入子组件的函数。子组件会将相应的 props 作为参数传给它:
- 实际上,这已经和作用域插槽的最终代码编译结果、以及手动编写渲染函数时使用作用域插槽的方式非常类似了。
v-slot="slotProps"
可以类比这里的函数签名,和函数的参数类似,我们也可以在 v-slot 中使用解构: