一.什么是插槽
插槽是 vue.js 开发员人根据从 Web Components 规范草案中获取的灵感,设计的一套 API,这套 API 允许我们对组件进行组合。
插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。
类似于 React.js 中的组合(children prop),插槽允许将一些其他的组件作为子组件传递给指定组件。
二.具名插槽
上面我们使用的是默认插槽,它是没有名字的,那如果一个组件内部,它将使用多个slot时,那我们怎么知道把某些代码片段放到那个slot中呢?那这个时候,我们就需要使用到具名插槽.所谓的具名插槽,就是给每个slot标签添加一个name属性.
上面我们使用的是默认插槽,它是没有名字的,那如果一个组件内部,它将使用多个slot时,那我们怎么知道把某些代码片段放到那个slot中呢?那这个时候,我们就需要使用到具名插槽.所谓的具名插槽,就是给每个slot标签添加一个name属性.
比如:在一个 <BaseLayout>
组件中,有如下模板:
<div class="container">
<header>
<!-- 标题内容放这里 -->
</header>
<main>
<!-- 主要内容放这里 -->
</main>
<footer>
<!-- 底部内容放这里 -->
</footer>
</div>
对于这种场景,<slot>
元素可以有一个特殊的 属性name
,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
这类带 name
的插槽被称为具名插槽 (named slots)。没有提供 name
的 <slot>
出口会隐式地命名为“default”。
在父组件中使用 <BaseLayout>
时,我们需要一种方式将多个插槽内容传入到各自目标插槽的出口。此时就需要用到具名插槽了:
要为具名插槽传入内容,我们需要使用一个含 v-slot
指令的 <template>
元素,并将目标插槽的名字传给该指令:
<BaseLayout>
<template v-slot:header>
<!-- header 插槽的内容放这里 -->
</template>
</BaseLayout>
注意:
三.作用域插槽
在上面的[注意]中我们讨论到,插槽的内容无法访问到子组件的状态.然而在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。
3.1 默认作用域插槽
我们也确实有办法这么做!可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes:
<!-- <MyComponent> 的模板 -->
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div>
当需要接收插槽 props 时,默认插槽和具名插槽的使用方式有一些小区别。下面我们将先展示默认插槽如何接受 props,通过子组件标签上的 v-slot
指令,直接接收到了一个插槽 props 对象:
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
3.2 具名作用域插槽
具名作用域插槽的工作方式也是类似的,插槽 props 可以作为 v-slot
指令的值被访问到:v-slot:name="slotProps"
。当使用缩写时是这样:
<MyComponent>
<template #header="headerProps">
{{ headerProps }}
</template>
<template #default="defaultProps">
{{ defaultProps }}
</template>
<template #footer="footerProps">
{{ footerProps }}
</template>
</MyComponent>
向具名插槽中传入 props:
<slot name="header" message="hello"></slot>
注意插槽上的 name
是一个 Vue 特别保留的 attribute,不会作为 props 传递给插槽。因此最终 headerProps
的结果是 { message: 'hello' }
。
如果你混用了具名插槽与默认插槽,则需要为默认插槽使用显式的 <template>
标签。尝试直接为组件添加 v-slot
指令将导致编译错误。这是为了避免因默认插槽的 props 的作用域而困惑。
正确的写法:
<MyComponent >
<template #default="a">
<p>
{{a.msg}}
</p>
<p>
{{a.count}}
</p>
</template>
<template #header>
<p>你好</p>
</template>
</MyComponent>