$derived
派生状态使用 $derived 符号声明:
<script>
let count = $state(0);
let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>
{doubled}
</button>
<p>{count} doubled is {doubled}</p>表达式内部 $derived(...) 应当无副作用。Svelte 将禁止在派生表达式中进行状态更改(例如 count++)。
与$state一样,您可以标记类字段为$derived。
[注意] Svelte 组件中的代码仅在创建时执行一次。如果没有
$derived运行,doubled即使在count变化时也会保持其原始值。
$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)。这可以用于诸如乐观的用户界面等情况,其中值是从“真相来源”(例如您的服务器上的数据)派生出来的,但您希望向用户显示即时反馈:
<script>
let { post, like } = $props();
let likes = $derived(post.likes);
async function onclick() {
// increment the `likes` count immediately...
likes += 1;
// and tell the server, which will eventually update `post`
try {
await like();
} catch {
// failed! roll back the change
likes -= 1;
}
}
</script>
<button {onclick}>🧡 {likes}</button>[注意] 在 Svelte 5.25 之前,派生变量是只读的。
派生和反应性
不同于$state,它将对象和数组转换为深度响应式代理,$derived值保持原样。例如,在这种情况下...
let items = $state([...]);
let index = $state(0);
let selected = $derived(items[index]);...您可以更改(或 绑定:)选定的属性,这将影响底层的items数组。如果items不是深度响应式的,修改selected将没有效果。
解构
如果您使用与$derived声明一起的解构,则生成的所有变量都将具有响应性——这...
let { let a: numbera, let b: numberb, let c: numberc } = function $derived<{
a: number;
b: number;
c: number;
}>(expression: {
a: number;
b: number;
c: number;
}): {
a: number;
b: number;
c: number;
}
namespace $derived
Declares derived state, i.e. one that depends on other state variables.
The expression inside $derived(...) should be free of side-effects.
Example:
let double = $derived(count * 2);
$derived(function stuff(): {
a: number;
b: number;
c: number;
}
stuff());...大致相当于这个:
let let _stuff: {
a: number;
b: number;
c: number;
}
_stuff = function $derived<{
a: number;
b: number;
c: number;
}>(expression: {
a: number;
b: number;
c: number;
}): {
a: number;
b: number;
c: number;
}
namespace $derived
Declares derived state, i.e. one that depends on other state variables.
The expression inside $derived(...) should be free of side-effects.
Example:
let double = $derived(count * 2);
$derived(function stuff(): {
a: number;
b: number;
c: number;
}
stuff());
let let a: numbera = function $derived<number>(expression: number): number
namespace $derived
Declares derived state, i.e. one that depends on other state variables.
The expression inside $derived(...) should be free of side-effects.
Example:
let double = $derived(count * 2);
$derived(let _stuff: {
a: number;
b: number;
c: number;
}
_stuff.a: numbera);
let let b: numberb = function $derived<number>(expression: number): number
namespace $derived
Declares derived state, i.e. one that depends on other state variables.
The expression inside $derived(...) should be free of side-effects.
Example:
let double = $derived(count * 2);
$derived(let _stuff: {
a: number;
b: number;
c: number;
}
_stuff.b: numberb);
let let c: numberc = function $derived<number>(expression: number): number
namespace $derived
Declares derived state, i.e. one that depends on other state variables.
The expression inside $derived(...) should be free of side-effects.
Example:
let double = $derived(count * 2);
$derived(let _stuff: {
a: number;
b: number;
c: number;
}
_stuff.c: numberc);更新传播
Svelte 使用一种称为推拉响应性的技术——当状态更新时,所有依赖于状态(无论是直接还是间接)的内容都会立即收到变更通知(即“推”),但派生值不会在实际上读取之前重新评估(即“拉”)。
如果派生值与其之前的值在引用上相同,则将跳过下游更新。换句话说,只有当 large 发生变化时,Svelte 才会更新按钮内的文本,而不是当 count 发生变化时,即使 large 依赖于 count:
<script>
let count = $state(0);
let large = $derived(count > 10);
</script>
<button onclick={() => count++}>
{large}
</button>