首页 / Svelte 5 入门教程 / 代码片段 Snippet 与 @render

Svelte 5 入门教程

代码片段 Snippet 与 @render

本教程共 50 篇 · 第 15 篇 · 更新于 2026-08-05 · 约 6 分钟阅读

SvelteSvelte 5模板语法Snippet@render

本节目标:掌握 Snippet 的定义和渲染、参数传递、children 默认片段,以及如何将 snippet 作为 props 传递给组件。学完你能在模板中复用标记片段,替代 Svelte 4 的 slot。

什么是 Snippet

Snippet(代码片段)是 Svelte 5 中可复用的模板片段。你可以把它理解为模板里的函数——定义一次,多次调用。

先看一个简单例子:

{#snippet sum(a, b)}
	<p>{a} + {b} = {a + b}</p>
{/snippet}

{@render sum(1, 2)}
{@render sum(3, 4)}
{@render sum(5, 6)}

输出:

1 + 2 = 3
3 + 4 = 7
5 + 6 = 11

{#snippet name()} 定义片段,{@render name()} 渲染它。就像定义函数和调用函数一样。

解决重复代码

Snippet 最直接的用途是消除重复标记。假设你有一组图片,有些带链接有些不带:

不使用 snippet(重复代码):

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

使用 snippet(消除重复):

{#snippet figure(image)}
	<figure>
		<img src={image.src} alt={image.caption} />
		<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}

Snippet 参数

Snippet 可以有任意多个参数,支持默认值和解构,但不支持 rest 参数(...args):

{#snippet greet(name, greeting = '你好')}
	<p>{greeting},{name}!</p>
{/snippet}

{@render greet('码上学')}
{@render greet('Svelte', 'Hello')}

参数可以解构:

{#snippet userInfo({ name, age })}
	<p>{name},{age}岁</p>
{/snippet}

{@render userInfo({ name: '码上学', age: 25 })}

Snippet 作用域

Snippet 可以引用外部变量(如 <script> 中声明的、{#each} 块中的),并且对同作用域的内容可见:

<script>
	let { message = '很高兴见到你!' } = $props();
</script>

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

<!-- 同一作用域可以调用 -->
{@render hello('Alice')}
{@render hello('Bob')}

Snippet 也可以自我引用或互相引用(递归):

{#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(5)}
<!-- 输出: 5... 4... 3... 2... 1... 🚀 -->

默认 children 片段

组件标签之间的内容自动成为 children snippet:

<!--- file: App.svelte --->
<Button>点击我</Button>
<!--- file: Button.svelte --->
<script>
	let { children } = $props();
</script>

<button>{@render children()}</button>
<!-- 渲染为 <button>点击我</button> -->

可选 children

如果 children 可能不存在,用可选链或条件渲染:

<!-- 方式一:可选链 -->
{@render children?.()}

<!-- 方式二:条件渲染 + 兜底内容 -->
{#if children}
	{@render children()}
{:else}
	<span>默认内容</span>
{/if}

将 Snippet 作为 Props 传递

隐式传递(推荐)

直接在组件标签内部声明 snippet,它们会自动成为组件的 props:

<!--- file: App.svelte --->
<script>
	import Table from './Table.svelte';

	const fruits = [
		{ name: '苹果', qty: 5, price: 2 },
		{ name: '香蕉', qty: 10, price: 1 }
	];
</script>

<Table data={fruits}>
	{#snippet header()}
		<th>水果</th>
		<th>数量</th>
		<th>单价</th>
		<th>合计</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>
<!--- file: Table.svelte --->
<script>
	let { data, header, row } = $props();
</script>

<table>
	{#if header}
		<thead><tr>{@render header()}</tr></thead>
	{/if}
	<tbody>
		{#each data as d}
			<tr>{@render row(d)}</tr>
		{/each}
	</tbody>
</table>

显式传递

也可以像普通 props 一样显式传递:

{#snippet mySnippet()}
	<p>内容</p>
{/snippet}

<MyComponent content={mySnippet} />
Note

隐式传递更简洁。只有当 snippet 需要在多个组件间共享时,才考虑显式声明后传递。

TypeScript 类型标注

Snippet 类型从 svelte 导入,用 Snippet 接口标注:

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

	interface Props {
		data: any[];
		children: Snippet;          // 无参数
		row: Snippet<[any]>;        // 接受一个参数
		optional?: Snippet;         // 可选 snippet
	}

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

Snippet 的泛型参数是元组类型,表示 snippet 的参数列表:

类型含义
Snippet无参数
Snippet<[any]>一个参数
Snippet<[string, number]>两个参数:string 和 number

用泛型让 datarow 类型关联:

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

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

Snippet vs Svelte 4 Slot

对比项Svelte 5 SnippetSvelte 4 Slot
定义方式{#snippet name()}<slot name="name">
渲染方式{@render name()}<slot name="name" />
参数传递name(arg1, arg2)let:arg
默认内容children + ?:<slot>默认</slot>
可组合性高(可递归、可互相引用)
类型安全有(Snippet 类型)
Tip

Slot 在 Svelte 5 中已弃用。新项目全部用 Snippet。如果你在迁移 Svelte 4 代码,参照上表一一替换即可。

导出 Snippet

<script module> 中导出 snippet,可以在其他组件中复用:

<!--- file: snippets.svelte --->
<script module>
	export { add };
</script>

{#snippet add(a, b)}
	{a} + {b} = {a + b}
{/snippet}
<!-- 使用 -->
<script>
	import { add } from './snippets.svelte';
</script>

{@render add(1, 2)}
Note

导出的 snippet 不能引用非模块 <script> 中的变量。这需要 Svelte 5.5.0 或更高版本。

本节回顾

  • {#snippet name(参数)} 定义代码片段,{@render name(参数)} 渲染
  • Snippet 像模板函数:可以有参数、默认值、解构,但不能用 rest 参数
  • 可以引用外部变量,可以递归和互相引用
  • 组件标签内的非 snippet 内容自动成为 children prop
  • 隐式传递:组件标签内声明 {#snippet} 自动成为 props
  • TypeScript 用 Snippet 接口标注,泛型参数是参数元组
  • Snippet 替代了 Svelte 4 的 slot,更灵活、有类型安全