插槽内容与出口
大家都知道在vue中组件能够接收任意类型的 JavaScript 值作为 props传递,那么如果我们想要在组件中接受模板内容或者其他组件内容,那么我们该如何接收呢?
没错,就是使用slot(插槽)。他该怎么使用呢,我们继续往下看。
举例来说,这里有一个 <FancyButton>
组件,可以像这样使用:
<FancyButton>
Click me! <!-- 插槽内容 -->
</FancyButton>
而 <FancyButton>
的模板是这样的:
<button class="fancy-btn">
<slot></slot> <!-- 插槽出口 -->
</button>
最终渲染出的 DOM 是这样:
<button class="fancy-btn">Click me!</button>
通过使用插槽,<FancyButton>
仅负责渲染外层的 <button>
(以及相应的样式),而其内部的内容由父组件提供。
理解插槽的另一种方式是和下面的 JavaScript 函数作类比,其概念是类似的:
// 父元素传入插槽内容
FancyButton('Click me!')
// FancyButton 在自己的模板中渲染插槽内容
function FancyButton(slotContent) {
return `<button class="fancy-btn">
${slotContent}
</button>`
}
我们可以将slot理解为一个函数的参数,最终在指定的区域进行渲染,他和props的区别就在于props传递的是参数,而slot更多传递的是模版。
插槽内容可以是任意合法的模板内容,不局限于文本。例如我们可以传入多个元素,甚至是组件:
<FancyButton>
<span style="color:red">Click me!</span>
<AwesomeIcon name="plus" />
</FancyButton>
渲染作用域
插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。举例来说:
<span>{{ message }}</span>
<FancyButton>{{ message }}</FancyButton>
插槽内容无法访问子组件的数据。Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。换言之:
父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。
默认内容
在外部没有提供任何内容的情况下,可以为插槽指定默认内容。比如有这样一个 <SubmitButton>
组件:
<button type="submit">
<slot></slot>
</button>
如果我们想在父组件没有提供任何插槽内容时在 <button>
内渲染“Submit”,只需要将“Submit”写在 <slot>
标签之间来作为默认内容:
<button type="submit">
<slot>
Submit <!-- 默认内容 -->
</slot>
</button>
现在,当我们在父组件中使用 <SubmitButton>
且没有提供任何插槽内容时:
<SubmitButton />
“Submit”将会被作为默认内容渲染:
<button type="submit">Submit</button>
但如果我们提供了插槽内容:
<SubmitButton>Save</SubmitButton>
那么被显式提供的内容会取代默认内容:
<button type="submit">Save</button>
具名插槽
当我们想在组件中使用多个插槽时就可以使用具名插槽。简单来说就是给每一个slot分配一个唯一的 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>
<!-- main 插槽的内容(隐式的默认插槽) -->=
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<!-- 简写模式 -->
<template #footer>
<!-- footer 插槽的内容放这里 -->
</template>
</BaseLayout>
v-slot
有对应的简写 #
,因此 <template v-slot:header>
可以简写为 <template #header>
。其意思就是“将这部分模板片段传入子组件的 header 插槽中”。
作用域插槽
如果我们想要在父组件访问一些子组件内部的属性,这时就需要使用作用域插槽了。
具体的使用如下,像对组件传递 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>
具名作用域插槽
具名作用域插槽的工作方式也是类似的,插槽 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' }
。所以大家在传递参数时命名一定要尽可能规避这些关键字,例如:default,export,import等。
这些内容是不是看着很眼熟
没错,大部份都是官网抄来的。现在官网介绍写的越来越好了,很容易让人理解,大家可以多去看看官方的文档,帮助会很大。
官网地址:vue官网