$derived
Производное состояние объявляется с помощью руны $derived
:
<script>
let count = $state(0);
let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>
{doubled}
</button>
<p>{count} вдвое больше — это {doubled}</p>
Выражение внутри $derived(...)
должно быть свободно от побочных эффектов. Svelte не позволит изменения состояния (например, count++
) внутри производных выражений.
Как и при использовании $state
, вы можете пометить поля класса как $derived
.
$derived.by
Иногда вам нужно создать сложные производные, которые не помещаются в короткое выражение. В таких случаях вы можете использовать $derived.by
, который принимает функцию в качестве аргумента.
<script>
let numbers = $state([1, 2, 3]);
let total = $derived.by(() => {
let total = 0;
for (const n of numbers) {
total += n;
}
return total;
});
</script>
<button onclick={() => numbers.push(numbers.length + 1)}>
{numbers.join(' + ')} = {total}
</button>
По сути, $derived(expression)
эквивалентно $derived.by(() => expression)
.
Понимание зависимостей
Всё, что читается синхронно внутри выражения $derived
(или тела функции $derived.by
), считается зависимостью производного состояния. Когда состояние изменяется, производное будет помечено как грязное и пересчитано при следующем чтении.
Чтобы исключить часть состояния из обработки как зависимость, используйте untrack
.
Переопределение производных значений
Производные выражения пересчитываются при изменении их зависимостей, но вы можете временно переопределить их значения, присвоив им новое значение (если они не объявлены с помощью const
). Это может быть полезно для таких вещей, как оптимистичный UI, где значение вычисляется на основе «источника истины» (например, данных с сервера), но вы хотите сразу показать пользователю обратную связь:
<script>
let { post, like } = $props();
let likes = $derived(post.likes);
async function onclick() {
// Немедленно увеличиваем счетчик `likes`...
likes += 1;
// и уведомляем сервер, который в конечном итоге обновит `post`
try {
await like();
} catch {
// Ошибка! Откатываем изменение
likes -= 1;
}
}
</script>
<button {onclick}>🧡 {likes}</button>
Производные значения и реактивность
В отличие от $state
, который преобразует объекты и массивы в глубоко реактивные прокси, значения $derived
остаются как есть. Например, в таком случае…
let items = $state([...]);
let index = $state(0);
let selected = $derived(items[index]);
…вы можете изменять (или использовать bind:
для) свойства selected
, и это повлияет на базовый массив items
. Если бы items
не был глубоко реактивным, изменение selected
не имело бы эффекта.
Обновление данных
Svelte использует механизм, известный как реактивность с пуш-пулом — когда состояние изменяется, все, что связано с этим состоянием (прямо или косвенно), немедленно получает уведомление об изменении (пуш), но производные значения не пересчитываются, пока они не будут фактически запрошены (пул).
Если новое значение производного совпадает с предыдущим по ссылке, обновления для зависимых компонентов будут пропущены. То есть, Svelte обновит текст внутри кнопки только тогда, когда изменится large
, а не count
, даже несмотря на то, что large
зависит от count
:
<script>
let count = $state(0);
let large = $derived(count > 10);
</script>
<button onclick={() => count++}>
{large}
</button>