Skip to content

Snippets 与 Slots

在 Svelte 5 中,Snippets(片段) 取代了旧的 <slot> 机制,成为向组件传递模板内容的标准方式。Snippets 更强大:可以带参数、可以被多次渲染、可以像值一样传递。

从 Slots 说起

Svelte 4 用 <slot> 接收父组件传入的内容:

svelte
<!-- Svelte 4(已过时) -->
<!-- Card.svelte -->
<div class="card">
  <slot />
</div>

Svelte 5 用 {@render children()} 来渲染默认内容,children 是一个特殊的 prop:

svelte
<!-- Svelte 5 -->
<!-- Card.svelte -->
<script>
  let { children } = $props();
</script>

<div class="card">
  {@render children()}
</div>

使用默认 Snippet

父组件把标签内的内容作为默认 snippet 传入:

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

<Card>
  <p>这些内容会渲染到 Card 内部</p>
</Card>

具名 Snippets

{#snippet 名字()} 定义具名片段,作为同名 prop 传给子组件:

svelte
<!-- Card.svelte -->
<script>
  let { children, header, footer } = $props();
</script>

<div class="card">
  {#if header}
    <div class="header">{@render header()}</div>
  {/if}
  <div class="body">{@render children()}</div>
  {#if footer}
    <div class="footer">{@render footer()}</div>
  {/if}
</div>
svelte
<!-- 父组件 -->
<Card>
  {#snippet header()}
    <h3>卡片标题</h3>
  {/snippet}

  <p>这是主体内容(默认 snippet)</p>

  {#snippet footer()}
    <small>页脚信息</small>
  {/snippet}
</Card>

带参数的 Snippets

Snippets 最强大的地方:可以接收参数。这让它们能渲染动态内容:

svelte
<!-- List.svelte -->
<script>
  let { items, row } = $props();
</script>

<ul>
  {#each items as item, i (i)}
    <!-- 把每个 item 传给父组件定义的 row snippet -->
    <li>{@render row(item, i)}</li>
  {/each}
</ul>
svelte
<!-- 父组件 -->
<script>
  import List from './List.svelte';
  let users = $state([
    { name: '小明', age: 20 },
    { name: '小红', age: 22 }
  ]);
</script>

<List items={users}>
  {#snippet row(user, index)}
    <strong>{index + 1}.</strong> {user.name}({user.age} 岁)
  {/snippet}
</List>

这里 List 负责遍历,但每一行长什么样由父组件决定 —— 这是一种非常灵活的控制反转模式(类似 React 的 render props)。

Snippets 是值

Snippets 可以存进变量、作为 props 传递、甚至放进 $state

svelte
<script>
  let { content } = $props();
  // content 是一个 snippet 值,可以在多处渲染
</script>

<div>{@render content()}</div>
<hr />
<div>{@render content()}</div>

为什么 Snippets 比 Slots 好

能力<slot>(Svelte 4)Snippets(Svelte 5)
带参数渲染{@render row(item)}
多次渲染同一片段有限
作为值传递/存储
条件渲染片段别扭{#if header}
类型检查强(配合 TS)

试一试

下面的卡片组件综合演示了具名 snippets(header / footer)和默认 snippet(children),并且 footer 里能读到父组件的状态。点点赞试试:

Snippets · {@render}Svelte 5 · Live

对应源码(子组件 Card.svelte):

svelte
<script>
  let { children, header, footer } = $props();
</script>

<div class="card">
  {#if header}<div class="card__header">{@render header()}</div>{/if}
  <div class="card__body">{@render children()}</div>
  {#if footer}<div class="card__footer">{@render footer()}</div>{/if}
</div>

父组件传入三个 snippets:

svelte
<Card>
  {#snippet header()}📦 Svelte 5 Snippets{/snippet}

  <p>这是 children 默认片段</p>
  <button onclick={() => likes++}>👍 点赞 {likes}</button>

  {#snippet footer()}当前点赞 {likes}{/snippet}
</Card>

小结

要点说明
{@render children()}渲染默认内容(取代 <slot>
{#snippet name()}定义具名片段
{@render row(item)}渲染带参数的片段
片段即值可传递、存储、多次渲染
控制反转子组件控制何时/如何渲染父组件提供的模板

下一步:事件处理

基于 VitePress 构建 · 示例使用 Svelte 5.57