{#snippet ...}
{#snippet name()}...{/snippet}{#snippet name(param1, param2, paramN)}...{/snippet}Сниппеты и теги render — это способ создавать многократно используемые блоки разметки внутри ваших компонентов. Вместо того чтобы писать повторяющийся код, как здесь…
{#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}Как и функции, сниппеты могут принимать любое количество параметров, у которых могут быть значения по умолчанию, и вы можете деструктурировать эти параметры. Однако вы не можете использовать остаточные параметры (rest).
Область видимости сниппетов
Заголовок раздела «Область видимости сниппетов»Сниппеты могут быть объявлены в любом месте внутри вашего компонента. Они могут ссылаться на значения, объявленные вне их самих, например, в теге <script> или в блоках {#each ...}…
<script> let { message = `Приятно тебя видеть!` } = $props();</script>
{#snippet hello(name)} <p>Привет, {name}! {message}!</p>{/snippet}
{@render hello('alice')}{@render hello('bob')}<script lang="ts"> let { message = `Приятно тебя видеть!` } = $props();</script>
{#snippet hello(name)} <p>Привет, {name}! {message}!</p>{/snippet}
{@render hello('alice')}{@render hello('bob')}…и они «видимы» для всего в той же области видимости (то есть для соседних элементов и их потомков):
<div> {#snippet x()} {#snippet y()}...{/snippet}
<!-- это сработает --> {@render y()} {/snippet}
<!-- это вызовет ошибку, так как `y` не находится в области видимости --> {@render y()}</div>
<!-- это также вызовет ошибку, так как `x` не находится в области видимости -->{@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} /><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>
<style> table { text-align: left; border-spacing: 0; }
tbody tr:nth-child(2n+1) { background: ButtonFace; }
table :global(th), table :global(td) { padding: 0.5em; }</style><script lang="ts"> 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} /><script lang="ts"> 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>
<style> table { text-align: left; border-spacing: 0; }
tbody tr:nth-child(2n+1) { background: ButtonFace; }
table :global(th), table :global(td) { padding: 0.5em; }</style>Думайте об этом как о передаче содержимого, а не данных, в компонент. Концепция похожа на слоты в веб-компонентах.
Неявные пропсы
Заголовок раздела «Неявные пропсы»Для удобства написания кода сниппеты, определённые внутри компонента, автоматически обрабатываются как пропсы этого компонента:
<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>
<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><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>
<style> table { text-align: left; border-spacing: 0; }
tbody tr:nth-child(2n+1) { background: ButtonFace; }
table :global(th), table :global(td) { padding: 0.5em; }</style><script lang="ts"> 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>
<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><script lang="ts"> 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>
<style> table { text-align: left; border-spacing: 0; }
tbody tr:nth-child(2n+1) { background: ButtonFace; }
table :global(th), table :global(td) { padding: 0.5em; }</style>Неявный сниппет children
Заголовок раздела «Неявный сниппет children»Любой контент внутри тегов компонента, который не является объявлением сниппета, неявно становится частью сниппета children:
<script> import Button from './Button.svelte';</script>
<Button>нажми меня</Button><script> let { children } = $props();</script>
<!-- Результатом будет <button>нажми меня</button> --><button>{@render children()}</button><script lang="ts"> import Button from './Button.svelte';</script>
<Button>нажми меня</Button><script lang="ts"> let { children } = $props();</script>
<!-- Результатом будет <button>нажми меня</button> --><button>{@render children()}</button>Необязательные пропсы сниппетов
Заголовок раздела «Необязательные пропсы сниппетов»Вы можете объявить пропсы сниппетов как необязательные. Используйте опциональную цепочку, чтобы не отображать ничего, если сниппет не задан…
<script> let { children } = $props();</script>
{@render children?.()}…или используйте блок #if, чтобы отобразить альтернативный контент:
<script> let { children } = $props();</script>
{#if children} {@render children()}{:else} альтернативный контент{/if}Типизация сниппетов
Заголовок раздела «Типизация сниппетов»Сниппеты реализуют интерфейс Snippet, импортируемый из '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 и сниппета row. Обратите внимание, что аргумент типа, переданный в Snippet, является кортежем, так как сниппеты могут иметь несколько параметров.
Мы можем ещё больше улучшить типизацию, объявив универсальный тип, чтобы data и row ссылались на один и тот же тип:
<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> import { add } from './snippets.svelte';</script>
{@render add(1, 2)}<script module> export { add };</script>
{#snippet add(a, b)} {a} + {b} = {a + b}{/snippet}<script lang="ts"> import { add } from './snippets.svelte';</script>
{@render add(1, 2)}<script module> export { add };</script>
{#snippet add(a, b)} {a} + {b} = {a + b}{/snippet}Программные сниппеты
Заголовок раздела «Программные сниппеты»Сниппеты могут быть созданы программно с помощью API createRawSnippet. Эта функция предназначена для продвинутых случаев использования.
Сниппеты и слоты
Заголовок раздела «Сниппеты и слоты»В Svelte 4 содержимое можно передавать компонентам с помощью слотов. Сниппеты более мощные и гибкие, поэтому слоты были объявлены устаревшими в Svelte 5.