前言
Svelte,一个非常“有趣”、用起来“很爽”的前端框架。从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1:

Svelte 以其独特的编译时优化机制著称,具有轻量级、高性能、易上手等特性,非常适合构建轻量级 Web 项目,也是我做个人项目的首选技术栈。
目前 Svelte 基于 Svelte 5 发布了最新的官方文档,但却缺少对应的中文文档。为了帮助大家学习 Svelte,为爱发电翻译了官方文档。
我同时搭建了 Svelte 最新的中文文档站点:https://svelte.yayujs.com ,如果需要辅助学习,也可以入手我的小册《Svelte 开发指南》,语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!
虽说是翻译,但个人并不喜欢严格遵守原文,为了保证中文阅读流畅,会删减部分语句,对难懂的部分也会另做补充解释,希望能给大家带来一个好的中文学习体验。
欢迎围观我的“朋友圈”、加入“低调务实优秀中国好青年”前端社群,分享技术,带你成长。
snippet
1{#snippet name()}...{/snippet}
1{#snippet name(param1, param2, paramN)}...{/snippet}
代码片段和 渲染标签 是在组件内部创建可复用标记块的一种方法。与其编写这样的重复代码...
1{#each images as image} 2 {#if image.href} 3 <a href={image.href}> 4 <figure> 5 <img src={image.src} alt={image.caption} width={image.width} height={image.height} /> 6 <figcaption>{image.caption}</figcaption> 7 </figure> 8 </a> 9 {:else} 10 <figure> 11 <img src={image.src} alt={image.caption} width={image.width} height={image.height} /> 12 <figcaption>{image.caption}</figcaption> 13 </figure> 14 {/if} 15{/each}
...你可以这样写:
1{#snippet figure(image)} 2 <figure> 3 <img src={image.src} alt={image.caption} width={image.width} height={image.height} /> 4 <figcaption>{image.caption}</figcaption> 5 </figure> 6{/snippet} 7 8{#each images as image} 9 {#if image.href} 10 <a href={image.href}> 11 {@render figure(image)} 12 </a> 13 {:else} 14 {@render figure(image)} 15 {/if} 16{/each}
像函数声明一样,代码片段可以有任意数量的参数,这些参数可以有默认值,并且你可以对每个参数进行解构。然而,你不能使用剩余参数。
代码片段作用域
代码片段可以在组件的任何地方声明。它们可以引用在自身之外声明的值,例如在 <script> 标签或 {#each ...} 块中 (demo)...
1<script> 2 let { message = `很高兴见到你!` } = $props(); 3</script> 4 5{#snippet hello(name)} 6 <p>你好 {name}! {message}!</p> 7{/snippet} 8 9{@render hello('alice')} 10{@render hello('bob')}
...并且它们对同一词法作用域中的所有内容都是"可见的"(即兄弟节点和这些兄弟节点的子节点):
1<div> 2 {#snippet x()} 3 {#snippet y()}...{/snippet} 4 5 <!-- 这很好 --> 6 {@render y()} 7 {/snippet} 8 9 <!-- 这将出错,因为 `y` 不在作用域中 --> 10 {@render y()} 11</div> 12 13<!-- 这也将出错,因为 `x` 不在作用域中 --> 14{@render x()}
代码片段可以引用自身和其他片段 (demo):
1{#snippet blastoff()} 2 <span>🚀</span> 3{/snippet} 4 5{#snippet countdown(n)} 6 {#if n > 0} 7 <span>{n}...</span> 8 {@render countdown(n - 1)} 9 {:else} 10 {@render blastoff()} 11 {/if} 12{/snippet} 13 14{@render countdown(10)}
将代码片段传递给组件
在模板中,代码片段和其他值一样。这样,它们可以作为 props 传递给组件 (demo):
1<script> 2 import Table from './Table.svelte'; 3 4 const fruits = [ 5 { name: '苹果', qty: 5, price: 2 }, 6 { name: '香蕉', qty: 10, price: 1 }, 7 { name: '樱桃', qty: 20, price: 0.5 } 8 ]; 9</script> 10 11{#snippet header()} 12 <th>水果</th> 13 <th>数量</th> 14 <th>价格</th> 15 <th>总计</th> 16{/snippet} 17 18{#snippet row(d)} 19 <td>{d.name}</td> 20 <td>{d.qty}</td> 21 <td>{d.price}</td> 22 <td>{d.qty * d.price}</td> 23{/snippet} 24 25<Table data={fruits} {header} {row} />
把它想象成向组件传递内容而非数据。这个概念类似于 Web 组件中的插槽。
为了方便,直接在组件内部声明的代码片段会隐式成为组件的 props (demo):
1<!-- 这在语义上与上面的相同 --> 2<Table data={fruits}> 3 {#snippet header()} 4 <th>水果</th> 5 <th>数量</th> 6 <th>价格</th> 7 <th>总计</th> 8 {/snippet} 9 10 {#snippet row(d)} 11 <td>{d.name}</td> 12 <td>{d.qty}</td> 13 <td>{d.price}</td> 14 <td>{d.qty * d.price}</td> 15 {/snippet} 16</Table>
组件标签内的任何不是代码片段声明的内容都将隐式成为 children 代码片段的一部分 (demo):
1<!--- file: App.svelte ---> 2<Button>点击我</Button>
1<!--- file: Button.svelte ---> 2<script> 3 let { children } = $props(); 4</script> 5 6<!-- 结果将是 <button>点击我</button> --> 7<button>{@render children()}</button>
[!NOTE] 请注意,如果组件内部还有内容,你不能有名为
children的 prop — 基于这个原因,应该避免使用这个名称作为 prop
你可以将代码片段 props 声明为可选的。你可以使用可选链,当代码片段未设置时不渲染任何内容...
1<script> 2 let { children } = $props(); 3</script> 4 5{@render children?.()}
...或者使用 #if 块来渲染后备内容:
1<script> 2 let { children } = $props(); 3</script> 4 5{#if children} 6 {@render children()} 7{:else} 8 后备内容 9{/if}
代码片段类型
代码片段实现了从 'svelte' 导入的 Snippet 接口:
1<script lang="ts"> 2 import type { Snippet } from 'svelte'; 3 4 interface Props { 5 data: any[]; 6 children: Snippet; 7 row: Snippet<[any]>; 8 } 9 10 let { data, children, row }: Props = $props(); 11</script>
通过这项改动,如果你尝试在没有提供 data prop 和 row 代码片段的情况下使用该组件,则会出现红色波浪线。请注意,提供给 Snippet 的类型参数是一个元组,因为代码片段可以有多个参数。
我们可以通过声明泛型来进一步收窄类型,以便 data 和 row 引用相同的类型:
1<script lang="ts" generics="T"> 2 import type { Snippet } from 'svelte'; 3 4 let { 5 data, 6 children, 7 row 8 }: { 9 data: T[]; 10 children: Snippet; 11 row: Snippet<[T]>; 12 } = $props(); 13</script>
导出代码片段
在 .svelte 文件顶层声明的代码片段可以从 <script module> 导出以供其他组件使用,前提是它们不引用非模块 <script> 中的任何声明(无论是直接引用还是通过其他代码片段间接引用) (demo):
1<script module> 2 export { add }; 3</script> 4 5{#snippet add(a, b)} 6 {a} + {b} = {a + b} 7{/snippet}
[!NOTE] 这需要 Svelte 5.5.0 或更新版本
程序化代码片段
代码片段可以通过 createRawSnippet API 以编程方式创建。这适用于高级用例。
代码片段和插槽
在 Svelte 4 中,可以使用 插槽 将内容传递给组件。代码片段更强大、更灵活,因此在 Svelte 5 中插槽已被弃用。
{@render ...}
要渲染一个代码片段,请使用 {@render ...} 标签。
1{#snippet sum(a, b)} 2 <p>{a} + {b} = {a + b}</p> 3{/snippet} 4 5{@render sum(1, 2)} 6{@render sum(3, 4)} 7{@render sum(5, 6)}
表达式可以是像 sum 这样的标识符,也可以是任意的 JavaScript 表达式:
1{@render (cool ? coolSnippet : lameSnippet)()}
可选代码片段
如果代码片段可能未定义 — 例如,因为它是一个传入的 prop — 那么你可以使用可选链操作符,只在代码片段确实存在时才渲染它:
1{@render children?.()}
或者,使用{#if ...} 块配合 :else 子句来渲染后备内容:
1{#if children} 2 {@render children()} 3{:else} 4 <p>后备内容</p> 5{/if}
