Skip to main content

{#snippet ...}

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

Snippet 和 渲染标签 是一种在组件内部创建可重用标记块的方法。不再需要像 this 那样编写重复的代码...

¥Snippets, and render tags, are a way to create reusable chunks of markup inside your components. Instead of writing duplicative code like this...

{#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}

...你可以写 this:

¥...you can write this:

{#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}

与函数声明一样,代码片段可以有任意数量的参数,这些参数可以有默认值,并且你可以解构每个参数。但是,你不能使用 rest 参数。

¥Like function declarations, snippets can have an arbitrary number of parameters, which can have default values, and you can destructure each parameter. You cannot use rest parameters, however.

代码片段范围(Snippet scope)

¥Snippet scope

Snippet 可以在组件内部的任何位置声明。它们可以引用在自身之外声明的值,例如在 <script> 标记或 {#each ...} 块 (demo) 中...

¥Snippets can be declared anywhere inside your component. They can reference values declared outside themselves, for example in the <script> tag or in {#each ...} blocks (demo)...

<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')}

...并且它们对于同一词汇范围内的所有内容都是 ‘visible’(即兄弟姐妹和这些兄弟姐妹的子项):

¥...and they are ‘visible’ to everything in the same lexical scope (i.e. siblings, and children of those siblings):

<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 可以引用自身和彼此 (demo):

¥Snippets can reference themselves and each other (demo):

{#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)}

将片段传递给组件(Passing snippets to components)

¥Passing snippets to components

在模板中,片段与其他片段一样都是值。因此,它们可以传递作为 props (demo) 传递给组件:

¥Within the template, snippets are values just like any other. As such, they can be passed to components as props (demo):

<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 组件中的插槽。

¥Think about it like passing content instead of data to a component. The concept is similar to slots in web components.

为了方便创作,直接在组件内部声明的代码片段会隐式成为组件(demo)上的 props:

¥As an authoring convenience, snippets declared directly inside a component implicitly become props on the component (demo):

<!-- 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 片段。

¥Any content inside the component tags that is not a snippet declaration implicitly becomes part of the children snippet (demo):

App
<Button>click me</Button>
Button
<script>
	let { children } = $props();
</script>

<!-- result will be <button>click me</button> -->
<button>{@render children()}</button>
<script lang="ts">
	let { children } = $props();
</script>

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

请注意,如果组件内还有内容,则不能有名为 children 的属性 — 因此,你应该避免使用该名称的属性

¥[!NOTE] Note that you cannot have a prop called children if you also have content inside the component — for this reason, you should avoid having props with that name

你可以将代码片段属性声明为可选。如果未设置代码片段,你可以使用可选链接不渲染任何内容...

¥You can declare snippet props as being optional. You can either use optional chaining to not render anything if the snippet isn’t set...

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

{@render children?.()}

...或使用 #if 块来渲染后备内容:

¥...or use an #if block to render fallback content:

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

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

键入片段(Typing snippets)

¥Typing snippets

Snippet 实现从 'svelte' 导入的 Snippet 接口:

¥Snippets implement the Snippet interface imported from 'svelte':

<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 prop 和 row 代码片段的情况下使用该组件,则会出现红色波浪线。请注意,提供给 Snippet 的类型参数是一个元组,因为代码片段可以有多个参数。

¥With this change, red squigglies will appear if you try and use the component without providing a data prop and a row snippet. Notice that the type argument provided to Snippet is a tuple, since snippets can have multiple parameters.

我们可以通过声明泛型来进一步收紧,以便 datarow 引用相同的类型:

¥We can tighten things up further by declaring a generic, so that data and row refer to the same type:

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

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

导出代码片段(Exporting snippets)

¥Exporting snippets

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

¥Snippets declared at the top level of a .svelte file can be exported from a <script module> for use in other components, provided they don’t reference any declarations in a non-module <script> (whether directly or indirectly, via other snippets) (demo):

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

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

这需要 Svelte 5.5.0 或更新版本

¥[!NOTE] This requires Svelte 5.5.0 or newer

程序化代码片段(Programmatic snippets)

¥Programmatic snippets

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

¥Snippets can be created programmatically with the createRawSnippet API. This is intended for advanced use cases.

代码片段和插槽(Snippets and slots)

¥Snippets and slots

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

¥In Svelte 4, content can be passed to components using slots. Snippets are more powerful and flexible, and as such slots are deprecated in Svelte 5.

上一页 下一页