Логика шаблонов: if, each и await
Блоки {#if}, {#each} и {#await} добавляют логику прямо в разметку — условия, циклы и асинхронность.
«Разметка должна уметь думать.» Логические блоки Svelte позволяют выражать условия и циклы декларативно, не покидая шаблон.
Интерфейс редко статичен: то нужно показать блок при условии, то отрисовать список, то дождаться данных. В Svelte для этого есть логические блоки — особые конструкции в фигурных скобках с решёткой. Они читаются почти как обычный псевдокод и компилируются в эффективные адресные обновления.
Условие — это {#if}...{:else if}...{:else}...{/if}. Цикл — {#each items as item}...{/each}, причём крайне желательно указывать ключ: {#each items as item (item.id)}, чтобы Svelte точечно обновлял именно изменившиеся строки, а не пересоздавал список. Асинхронность — {#await promise}, который умеет показывать состояние загрузки, успех и ошибку.
<script>
let items = $state([{ id: 1, text: 'Молоко' }, { id: 2, text: 'Хлеб' }]);
let show = $state(true);
</script>
{#if show}
<ul>
{#each items as item (item.id)}
<li>{item.text}</li>
{:else}
<li>Список пуст</li>
{/each}
</ul>
{:else}
<p>Список скрыт</p>
{/if}Блок {:else} внутри {#each} срабатывает, когда массив пуст — удобный способ показать заглушку. А {#await} особенно элегантен для запросов:
{#await fetchUser()}
<p>Загрузка...</p>
{:then user}
<p>Привет, {user.name}</p>
{:catch error}
<p>Ошибка: {error.message}</p>
{/await}Как это работает под капотом
Компилятор превращает каждый блок в управляющий код, который создаёт и удаляет узлы по условию или перебирает массив. Для {#each} с ключом Svelte ведёт сопоставление «ключ → узел» и при изменении массива переиспользует существующие узлы. Смоделируем выигрыш от ключей.
// Почему ключи в #each важны: переиспользование вместо пересоздания
let domNodes = new Map(); // ключ -> 'узел'
function render(items) {
let created = 0, reused = 0;
const next = new Map();
for (const it of items) {
if (domNodes.has(it.id)) { reused++; next.set(it.id, domNodes.get(it.id)); }
else { created++; next.set(it.id, 'node#' + it.id); }
}
domNodes = next;
console.log('создано:', created, 'переиспользовано:', reused);
}
render([{id:1},{id:2}]); // создано: 2 переиспользовано: 0
render([{id:2},{id:1},{id:3}]); // создано: 1 переиспользовано: 2 (порядок сменился!)Попробуй сам ▶ — вставь код в консоль браузера (F12 → Console) и нажми Enter, чтобы увидеть вывод.
массив items
|
v
{#each items as i (i.id)}
|
+-- ключ найден -> переиспользовать узел
+-- ключ новый -> создать узел
+-- ключ исчез -> удалить узелЧастые ошибки
- Не указывать ключ в
{#each}. Без него обновления списка медленнее и возможны баги с состоянием строк. - Брать индекс как ключ при перестановках. Лучше стабильный идентификатор элемента.
- Городить вычисления в разметке. Сложную логику выносите в
$derived.
Best practices
- Всегда задавайте стабильный ключ в циклах по данным с идентификаторами.
- Используйте
{#await}вместо ручных флагов загрузки — он чище и безопаснее. - Держите разметку декларативной; тяжёлую логику считайте заранее в скрипте.
Декларативная разметка против ручного DOM
Чтобы оценить логические блоки, вспомните, как условный рендеринг делали раньше — вручную: создавали элемент, проверяли условие, добавляли или удаляли узел из DOM, не забывали почистить за собой при обратном изменении. Это был источник бесконечных багов: забытое удаление, рассинхрон между состоянием и тем, что на экране, утечки обработчиков. Логические блоки Svelte превращают всё это в декларацию: вы просто пишете «если условие — покажи это, иначе — то», а движок берёт на себя создание и удаление узлов в нужный момент. То же самое с циклами и асинхронностью. Вы описываете желаемый результат для каждого состояния данных, а не последовательность шагов по его достижению. Особенно ярко это видно в блоке {#await}: три состояния асинхронной операции — загрузка, успех, ошибка — описываются рядом, наглядно, без ручных флагов и без риска показать устаревшие данные. Декларативность здесь не просто красиво — она устраняет целый класс ошибок.
Итог: логические блоки {#if}, {#each} и {#await} вносят условия, циклы и асинхронность прямо в разметку. Ключи в циклах включают точечные обновления и предотвращают баги.