插槽

插槽(Slots)

插槽是具名的输出口,你可以使用 @slot 标签在组件的开始与结束标签之间定义它。组件可以通过 $slots 对象以函数形式访问插槽。

让我们创建一个卡片(card)组件,并使用插槽来渲染卡片的不同区域。

views/components/card.edge
@let(attributes = $props
.merge({
class: ['card']
})
.toAttrs()
)
<div {{ attributes }}>
<div class="card_header">
{{{ await $slots.header() }}}
</div>
<div class="card_contents">
{{{ await $slots.content() }}}
</div>
</div>

现在,让我们使用 card 组件,并使用插槽定义其内容。

@card({ class: ['card-lg', 'card-shadow'] })
@slot('header')
<strong> Quick start </strong>
@end
@slot('content')
<p> Start building your next project in minutes </p>
@end
@end

主插槽与具名插槽

在前面的示例中,我们使用具名插槽为多个输出口定义内容。但是,如果一个组件只需要一个插槽,你可以使用 main 插槽。

主插槽指的是组件开始与结束标签之间的所有内容。在下面的示例中,我们接受一个 prop 作为卡片标题,并将其内容作为 main 插槽。

<div {{ attributes }}>
<div class="card_header">
{{ title }}
</div>
<div class="card_contents">
{{{ await $slots.main() }}}
</div>
</div>
@card({ title: 'Quick start' })
<p> Start building your next project in minutes </p>
@end

插槽作用域

使用 @slot 标签定义的插槽可以访问当前模板的状态。它们无法访问组件的状态。

在下面的示例中,我们定义了 cardSizesizes 变量。这些变量仅对组件可用,对父模板的插槽不可用。

views/components/card.edge
@let(cardSize = 'medium')
@let(sizes = {
medium: '350px',
small: '200px',
large: '450px'
})
<div class="{{ sizes[cardSize] }}">
<div class="card_header">
{{{ await $slots.header() }}}
</div>
<div class="card_contents">
{{{ await $slots.content() }}}
</div>
</div>
@card()
@slot('header')
<strong> Quick start </strong>
@end
@slot('content')
{{-- cardSize 的值将是 undefined --}}
<p> I am a {{ cardSize }} card </p>
@end
@end

然而,组件在渲染插槽时可以向插槽传递数据。我们来看下面的示例:

@let(cardSize = 'medium')
@let(sizes = {
medium: '350px',
small: '200px',
large: '450px'
})
<div class="{{ sizes[cardSize] }}">
<div class="card_header">
{{{ await $slots.header({ sizes, cardSize }) }}}
</div>
<div class="card_contents">
{{{ await $slots.content({ sizes, cardSize }) }}}
</div>
</div>

现在,你可以在 @slot 组件内部访问共享的状态,如下所示。

@card()
@slot('header')
<strong> Quick start </strong>
@end
@slot('content', componentState)
<p> I am a {{ componentState.cardSize }} card </p>
@end
@end