Svelte

`$derived`: производные значения

В корзине магазина есть массив товаров, а под ним надо показать итоговую сумму со скидкой и количество позиций. Эти числа не хранят отдельно - их вычисляют из корзины. Если завести для суммы своё состояние `$state` и обновлять его руками при каждом изменении корзины, рано или поздно где-то забудут пересчитать, и сумма соврёт. Руна `$derived` решает это: она объявляет значение как формулу от другого состояния, и Svelte сам пересчитывает его, когда меняется источник.

  • Итог корзины: сумма, скидка, число позиций вычисляются из массива товаров через `$derived`
  • Фильтрация и поиск: отфильтрованный список - производное от исходных данных и строки запроса
  • Валидация формы: флаг можноОтправить выводится из заполненности полей, а не хранится отдельно
  • Форматирование: отображаемая цена с разделителями - производная от числового значения
  • Прогресс-бары и счётчики, где показатель всегда есть функция от текущего состояния

Предварительные знания

  • Руна `$state` и понятие реактивного состояния
  • JavaScript: методы массивов reduce, filter, map
  • Идея чистой функции: результат зависит только от входов

Объявление производного значения

Руна `$derived` принимает выражение и возвращает значение, которое пересчитывается автоматически. Выражение читает другое реактивное состояние, и Svelte запоминает эти зависимости. Когда любой источник меняется, производное обновляется само. Читают его как обычную переменную, но присваивать ему нельзя: его значение полностью определяется формулой.

Здесь total зависит от price и qty, а withTax зависит от total. Производные образуют цепочку: при изменении qty Svelte пересчитает total, затем withTax, затем обновит разметку. Разработчик нигде не вызывает пересчёт вручную - формулы описывают зависимости, остальное делает компилятор.

  • Ручное состояние через `$state` — Сумму держат в отдельном `$state` и обновляют в каждом обработчике, меняющем корзину. Легко забыть одно место и получить рассинхрон
  • Производное через `$derived` — Сумма - формула от корзины. Единственный источник истины, пересчёт автоматический, рассинхрон невозможен

Производному значению обычно не присваивают напрямую (с Svelte 5.25 переприсвоение возможно для оптимистичного UI: значение временно переопределяется и пересчитается при следующем изменении зависимостей). Запись total равно 0 не имеет смысла: производное вычисляется только своей формулой. Если значение должно и вычисляться, и редактироваться вручную, это уже обычное `$state`, а не `$derived`.

Что произойдёт с переменной total из `$derived(price * qty)`, когда qty изменится?

`$derived.by` для сложных вычислений

Когда вычисление не умещается в одно выражение - есть ветвления, циклы, промежуточные переменные - используют форму `$derived.by`. Она принимает функцию без аргументов, которая возвращает результат. Внутри можно писать любой код, а Svelte отследит, какое реактивное состояние эта функция читает, и пересчитает результат при его изменении.

Функция перебирает массив, копит сумму и количество и возвращает объект. Это нельзя записать одним коротким выражением, поэтому `$derived.by` удобнее. Поведение то же, что у обычного `$derived`: результат кэшируется и пересчитывается только когда меняется состояние, которое функция читает - здесь массив items.

ФормаКогда применять
`$derived(выражение)`Одно выражение: арифметика, простой filter или map
`$derived.by(() => { ... })`Многошаговая логика: циклы, ветвления, промежуточные переменные

Функция в `$derived.by` должна быть чистой относительно реактивности: она читает состояние и возвращает значение, но не меняет другое состояние и не делает побочных эффектов. Если нужно что-то изменить во внешнем мире (запрос, запись в localStorage), это работа для `$effect`, а не для `$derived`.

Когда вместо `$derived(выражение)` стоит использовать `$derived.by`?

Ленивый пересчёт и отслеживание зависимостей

Пересчёт производного ленивый. Значение не вычисляется в момент изменения источника, а только при первом чтении после этого изменения. До тех пор результат не нужен, и Svelte его не считает. После вычисления значение кэшируется и переиспользуется, пока зависимости не поменялись снова. Это экономит работу, когда производное в данный момент нигде не отображается.

Отслеживание зависимостей автоматическое и точное: Svelte фиксирует ровно то состояние, которое формула реально прочитала во время вычисления. Если в выражении есть ветка if, и в текущем вычислении она не выполнилась, то состояние из той ветки в зависимости не попадёт, пока ветка не сработает. Это значит, что производное пересчитывается только на действительно влияющие изменения.

Точное отслеживание - причина, по которой производные дешевы. Цепочка из десятка производных не приводит к лавине пересчётов: меняется один источник, помечаются только зависящие от него производные, а считаются они лишь когда их значение кому-то понадобится.

Что означает ленивый пересчёт производного значения в `$derived`?

Связь с другими темами

`$derived` стоит между состоянием и его отображением:

  • `$state`: реактивное состояние — Производные значения вычисляются из источников `$state`
  • `$effect`: побочные эффекты — `$effect` тоже реагирует на состояние, но не возвращает значение, а выполняет действие
  • Руны: явная реактивность — `$derived` заменил вычисляемую часть метки доллар-двоеточие из Svelte 4

Итог

  • Руна `$derived` объявляет значение как формулу от другого реактивного состояния, без ручного пересчёта
  • Svelte сам отслеживает, какие источники читает выражение, и пересчитывает производное при их изменении
  • Для сложной логики с ветвлениями и циклами есть форма `$derived.by` с функцией, возвращающей значение
  • Пересчёт ленивый: производное вычисляется при чтении и кэшируется, пока зависимости не изменились
  • Производное состояние обычно не присваивают вручную - его источник истины это формула (с 5.25 допускается временное переопределение)

Связанные уроки

  • sv-06-state — `$derived` вычисляет значения из реактивного состояния, поэтому сначала нужен `$state`
  • sv-08-effect — `$effect` и `$derived` оба реагируют на состояние, но `$derived` возвращает значение, а `$effect` выполняет действие
  • sv-05-runes-intro — `$derived` пришёл на смену части возможностей метки доллар-двоеточие из Svelte 4
`$derived`: производные значения

0

1

Войти