Лучшие практики
Этот документ описывает некоторые лучшие практики, которые помогут вам писать быстрые и надёжные приложения на Svelte. Он также доступен как навык svelte-core-bestpractices для ваших агентов.
Используйте руну $state только для переменных, которые должны быть реактивными — то есть для переменных, изменение которых должно вызывать обновление $effect, $derived или выражений в шаблоне. Всё остальное можно оставить обычными переменными.
Объекты и массивы ($state({...}) или $state([...])) становятся глубоко реактивными, то есть мутация их содержимого будет запускать обновления. Здесь есть компромисс: за тонкую реактивность приходится платить тем, что объекты оборачиваются в прокси, а это создаёт накладные расходы на производительность. В случаях, когда вы работаете с большими объектами, которые только переприсваиваются целиком (а не мутируются), лучше использовать $state.raw. Это особенно часто встречается, например, при работе с ответами от API.
$derived
Заголовок раздела «$derived»Для вычисления значений на основе состояния используйте $derived, а не $effect:
// правильноlet square = $derived(num * num);
// неправильноlet square;
$effect(() => { square = num * num;});Derived-значения можно перезаписывать — вы можете присваивать им значения точно так же, как и $state, но при изменении их выражения они будут перевычисляться заново.
Если выражение внутри derived возвращает объект или массив, он возвращается как есть — он не становится глубоко реактивным. Однако в редких случаях, когда это всё-таки нужно, вы можете использовать $state внутри $derived.by.
$effect
Заголовок раздела «$effect»Эффекты — это запасной выход, и в большинстве случаев их лучше избегать. В частности, не обновляйте состояние внутри эффектов.
- Если нужно синхронизировать состояние с внешней библиотекой (например, D3), часто удобнее использовать
{@attach ...} - Если код должен выполняться в ответ на действие пользователя, размещайте его прямо в обработчике события или используйте привязку функции там, где это уместно
- Если нужно логировать значения для отладки, используйте
$inspect - Если требуется наблюдать за чем-то внешним по отношению к Svelte, используйте
createSubscriber
Никогда не оборачивайте содержимое эффекта в if (browser) {...} и подобные проверки — эффекты не выполняются на сервере.
Относитесь к пропсам так, будто они могут измениться в любой момент. Например, значения, зависящие от пропсов, в большинстве случаев должны использовать $derived:
let { type } = $props();
// правильноlet color = $derived(type === 'danger' ? 'red' : 'green');
// неправильно — `color` не обновится, если изменится `type`let color = type === 'danger' ? 'red' : 'green';$inspect.trace
Заголовок раздела «$inspect.trace»$inspect.trace — это инструмент отладки реактивности. Если что-то не обновляется должным образом или выполняется чаще, чем должно, вы можете добавить $inspect.trace(label) в качестве первой строки внутри $effect или $derived.by (или любой функции, которую они вызывают). Это позволит отследить их зависимости и выяснить, какая именно из них запустила обновление.
События
Заголовок раздела «События»Любой атрибут элемента, начинающийся с on, рассматривается как слушатель события:
<button onclick={() => {...}}>click me</button>
<!-- сокращённая запись атрибута тоже работает --><button {onclick}>...</button>
<!-- так же работают spread-атрибуты --><button {...props}>...</button>Если вам нужно прикрепить слушатели событий к window или document, вы можете использовать <svelte:window> и <svelte:document>:
<svelte:window onkeydown={...} /><svelte:document onvisibilitychange={...} />Избегайте использования onMount или $effect для этого.
Сниппеты
Заголовок раздела «Сниппеты»Сниппеты — это способ определить повторно используемые фрагменты разметки, которые можно вызывать с помощью тега {@render ...} или передавать компонентам через пропсы. Они должны быть объявлены внутри шаблона.
{#snippet greeting(name)} <p>привет, {name}!</p>{/snippet}
{@render greeting('world')}Блоки each
Заголовок раздела «Блоки each»Предпочитайте использовать блоки с привязкой по ключам — это значительно улучшает производительность, позволяя Svelte точечно вставлять или удалять элементы, вместо того чтобы обновлять DOM существующих элементов.
Избегайте деструктуризации, если вам нужно изменять (мутировать) элемент (например, при использовании bind:value={item.count} и подобных конструкций).
Использование JavaScript-переменных в CSS
Заголовок раздела «Использование JavaScript-переменных в CSS»Если у вас есть переменная JavaScript, которую вы хотите использовать внутри CSS, вы можете задать пользовательское свойство с помощью директивы style:.
<div style:--columns={columns}>...</div>Вы можете затем использовать var(--columns) внутри компонента <style>.
Стилизация дочерних компонентов
Заголовок раздела «Стилизация дочерних компонентов»CSS в теге компонента <style> ограничен областью видимости только этого компонента (scoped). Если родительскому компоненту нужно управлять стилями дочернего, предпочтительный способ — использовать пользовательские свойства CSS:
<Child --color="red" />
<!-- Child.svelte --><h1>Привет</h1>
<style> h1 { color: var(--color); }</style>Если это невозможно (например, дочерний компонент берётся из библиотеки), вы можете использовать :global, чтобы переопределить стили:
<div> <Child /></div>
<style> div :global { h1 { color: red; } }</style>Контекст
Заголовок раздела «Контекст»Рассматривайте использование контекста вместо объявления состояния в общем модуле. Это ограничит область действия состояния той частью приложения, которой оно действительно нужно, и исключит риск утечки состояния между пользователями при серверном рендеринге.
Используйте createContext вместо setContext и getContext, поскольку он обеспечивает типобезопасность.
Асинхронный Svelte
Заголовок раздела «Асинхронный Svelte»Если вы используете версию 5.36 или выше, вы можете применять await-выражения и данные с поддержкой гидратации, чтобы напрямую работать с промисами внутри компонентов.
Избегайте устаревших возможностей
Заголовок раздела «Избегайте устаревших возможностей»Всегда используйте режим рун (runes mode) для нового кода и старайтесь избегать возможностей, у которых уже есть более современные замены:
- используйте
$stateвместо неявной реактивности (например,let count = 0; count += 1) - используйте
$derivedи$effectвместо$:-присваиваний и$:-выражений (но применяйте эффекты только тогда, когда нет лучшего решения) - используйте
$propsвместоexport let,$$propsи$$restProps - используйте
onclick={...}вместоon:click={...} - используйте
{#snippet ...}и{@render ...}вместо<slot>,$$slotsи<svelte:fragment> - используйте
<DynamicComponent>вместо<svelte:component this={DynamicComponent}> - используйте
import Self from './ThisComponent.svelte'и<Self>вместо<svelte:self> - используйте классы с полями
$stateдля передачи реактивности между компонентами вместо использования stores - используйте
{@attach ...}вместоuse:action - используйте массивы и объекты в стиле clsx в атрибутах
classвместо директивыclass: