跳至主要内容

{#snippet ...}

{#snippet name()}...{/snippet}
{#snippet name(param1, param2, paramN)}...{/snippet}

代码段和 渲染标签 是一种在组件内部创建可重用标记块的方法。与其编写像 这样... 这样的重复代码。

{#each images as image}
	{#if image.href}
		<a href={image.href}>
			<figure>
				<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
				<figcaption>{image.caption}</figcaption>
			</figure>
		</a>
	{:else}
		<figure>
			<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
			<figcaption>{image.caption}</figcaption>
		</figure>
	{/if}
{/each}

...您可以编写 这样

{#snippet figure(image)}
	<figure>
		<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
		<figcaption>{image.caption}</figcaption>
	</figure>
{/snippet}

{#each images as image}
	{#if image.href}
		<a href={image.href}>
			{@render figure(image)}
		</a>
	{:else}
		{@render figure(image)}
	{/if}
{/each}

就像函数声明一样,代码段可以具有任意数量的参数,这些参数可以具有默认值,并且您可以解构每个参数。但是,您不能使用剩余参数。

代码段作用域

代码段可以在组件内的任何位置声明。它们可以引用在其自身外部声明的值,例如在 <script> 标签中或在 {#each ...} 块中(演示)...

<script>
	let { message = `it's great to see you!` } = $props();
</script>

{#snippet hello(name)}
	<p>hello {name}! {message}!</p>
{/snippet}

{@render hello('alice')}
{@render hello('bob')}

...并且它们对相同词法作用域中的所有内容(即兄弟姐妹以及这些兄弟姐妹的子级)都是“可见的”。

<div>
	{#snippet x()}
		{#snippet y()}...{/snippet}

		<!-- this is fine -->
		{@render y()}
	{/snippet}

	<!-- this will error, as `y` is not in scope -->
	{@render y()}
</div>

<!-- this will also error, as `x` is not in scope -->
{@render x()}

代码段可以引用自身和其他代码段(演示)

{#snippet blastoff()}
	<span>🚀</span>
{/snippet}

{#snippet countdown(n)}
	{#if n > 0}
		<span>{n}...</span>
		{@render countdown(n - 1)}
	{:else}
		{@render blastoff()}
	{/if}
{/snippet}

{@render countdown(10)}

将代码段传递给组件

在模板中,代码段就像任何其他值一样。因此,可以将它们作为道具传递给组件(演示)

<script>
	import Table from './Table.svelte';

	const fruits = [
		{ name: 'apples', qty: 5, price: 2 },
		{ name: 'bananas', qty: 10, price: 1 },
		{ name: 'cherries', qty: 20, price: 0.5 }
	];
</script>

{#snippet header()}
	<th>fruit</th>
	<th>qty</th>
	<th>price</th>
	<th>total</th>
{/snippet}

{#snippet row(d)}
	<td>{d.name}</td>
	<td>{d.qty}</td>
	<td>{d.price}</td>
	<td>{d.qty * d.price}</td>
{/snippet}

<Table data={fruits} {header} {row} />

可以将其视为向组件传递内容而不是数据。此概念类似于 Web 组件中的插槽。

为了方便编写,直接组件内部声明的代码段会隐式地成为组件上的道具(演示)

<!-- this is semantically the same as the above -->
<Table data={fruits}>
	{#snippet header()}
		<th>fruit</th>
		<th>qty</th>
		<th>price</th>
		<th>total</th>
	{/snippet}

	{#snippet row(d)}
		<td>{d.name}</td>
		<td>{d.qty}</td>
		<td>{d.price}</td>
		<td>{d.qty * d.price}</td>
	{/snippet}
</Table>

组件标签内部不是代码段声明的任何内容都会隐式地成为 children 代码段的一部分(演示)

应用
<Button>click me</Button>
按钮
<script>
	let { children } = $props();
</script>

<!-- result will be <button>click me</button> -->
<button>{@render children()}</button>

请注意,如果您在组件内部还有内容,则不能拥有名为 children 的道具——出于这个原因,您应该避免使用该名称的道具。

您可以将代码段道具声明为可选。您可以使用可选链来在未设置代码段时不呈现任何内容...

<script>
	let { children } = $props();
</script>

{@render children?.()}

...或者使用 #if 块来渲染备用内容。

<script>
	let { children } = $props();
</script>

{#if children}
	{@render children()}
{:else}
	fallback content
{/if}

代码段类型

代码段实现了从 'svelte' 导入的 Snippet 接口。

<script lang="ts">
	import type { Snippet } from 'svelte';

	interface Props {
		data: any[];
		children: Snippet;
		row: Snippet<[any]>;
	}

	let { data, children, row }: Props = $props();
</script>

通过此更改,如果您尝试在不提供 data 道具和 row 代码段的情况下使用该组件,则会出现红色波浪线。请注意,提供给 Snippet 的类型参数是元组,因为代码段可以具有多个参数。

我们可以通过声明泛型来进一步加强,以便 datarow 指向相同的类型。

<script lang="ts" generics="T">
	import type { Snippet } from 'svelte';

	let {
		data,
		children,
		row
	}: {
		data: T[];
		children: Snippet;
		row: Snippet<[T]>;
	} = $props();
</script>

导出代码段

.svelte 文件的顶层声明的代码段可以从 <script module> 导出以供其他组件使用,前提是它们不引用非模块 <script> 中的任何声明(无论是否直接或间接通过其他代码段)(演示)

<script module>
	export { add };
</script>

{#snippet add(a, b)}
	{a} + {b} = {a + b}
{/snippet}

这需要 Svelte 5.5.0 或更高版本。

编程代码段

可以使用 createRawSnippet API 以编程方式创建代码段。这适用于高级用例。

代码段和插槽

在 Svelte 4 中,可以使用 插槽 将内容传递给组件。代码段功能更强大且更灵活,因此插槽在 Svelte 5 中已弃用。

在 GitHub 上编辑此页面

上一页 下一页