插槽内容
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 将 <slot>
元素作为承载分发内容的出口。
它允许你像这样合成组件:
<todo-button>
Add todo
</todo-button>
然后在 <todo-button>
的模板中,你可能有:
<!-- todo-button 组件模板 -->
<button class="btn-primary">
<slot></slot>
</button>
当组件渲染的时候,将会被替换为“Add Todo”。
<!-- 渲染 HTML -->
<button class="btn-primary">
Add todo
</button>
不过,字符串只是开始!插槽还可以包含任何模板代码,包括 HTML:
<todo-button>
<!-- 添加一个Font Awesome 图标 -->
<i class="fas fa-plus"></i>
Add todo
</todo-button>
或其他组件
<todo-button>
<!-- 添加一个图标的组件 -->
<font-awesome-icon name="plus"></font-awesome-icon>
Add todo
</todo-button>
如果 <todo-button>
的 template 中没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃
<!-- todo-button 组件模板 -->
<button class="btn-primary">
Create a new item
</button>
<todo-button>
<!-- 以下文本不会渲染 -->
Add todo
</todo-button>
渲染作用域
当你想在一个插槽中使用数据时,例如:
<todo-button>
Delete a {{ item.name }}
</todo-button>
该插槽可以访问与模板其余部分相同的实例 property (即相同的“作用域”)。
插槽不能访问 <todo-button>
的作用域。例如,尝试访问 action
将不起作用:
<todo-button action="delete">
Clicking here will {{ action }} an item
<!-- `action` 未被定义,因为它的内容是传递*到* <todo-button>,而不是*在* <todo-button>里定义的。 -->
</todo-button>
作为一条规则,请记住:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
后备内容
有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 <submit-button>
组件中:
<button type="submit">
<slot></slot>
</button>
我们可能希望这个 <button>
内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在 <slot>
标签内:
<button type="submit">
<slot>Submit</slot>
</button>
现在当我在一个父级组件中使用 <submit-button
> 并且不提供任何插槽内容时:
<submit-button></submit-button>
后备内容“Submit”将会被渲染:
<button type="submit">
Submit
</button>
但是如果我们提供内容:
<submit-button>
Save
</submit-button>
则这个提供的内容将会被渲染从而取代后备内容:
<button type="submit">
Save
</button>
具名插槽
有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout>
组件:
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况,<slot>
元素有一个特殊的 attribute:name
。这个 attribute 可以用来定义额外的插槽:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name
的 <slot>
出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
现在 <template>
元素中的所有内容都将会被传入相应的插槽。
渲染的 HTML 将会是:
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>