到目前为止,我们已经讨论了状态方面的反应性。但这只是等式的一半 - 状态只有在某些东西对其做出反应时才会产生反应,否则它只是一个闪闪发光的变量。
¥So far we’ve talked about reactivity in terms of state. But that’s only half of the equation — state is only reactive if something is reacting to it, otherwise it’s just a sparkling variable.
做出反应的东西称为效果。你已经遇到了效果 - Svelte 代表你创建的效果,用于响应状态更改来更新 DOM - 但你也可以使用 $effect
符文创建自己的效果。
¥The thing that reacts is called an effect. You’ve already encountered effects — the ones that Svelte creates on your behalf to update the DOM in response to state changes — but you can also create your own with the $effect
rune.
大多数情况下,你不应该这样做。最好将
$effect
视为一个逃生出口,而不是经常使用的东西。例如,如果你可以将副作用放在 event handler 中,那几乎总是更好的选择。
假设我们想使用 setInterval
来跟踪组件的安装时间。创建效果:
¥Let’s say we want to use setInterval
to keep track of how long the component has been mounted. Create the effect:
<script>
let elapsed = $state(0);
let interval = $state(1000);
$effect(() => {
setInterval(() => {
elapsed += 1;
}, interval);
});
</script>
<script lang="ts">
let elapsed = $state(0);
let interval = $state(1000);
$effect(() => {
setInterval(() => {
elapsed += 1;
}, interval);
});
</script>
单击 ‘加速’ 按钮几次,注意 elapsed
的上升速度更快,因为每次 interval
变小时,我们都会调用 setInterval
。
¥Click the ‘speed up’ button a few times and notice that elapsed
ticks up faster, because we’re calling setInterval
each time interval
gets smaller.
如果我们然后单击 ‘减速’ 按钮...嗯,它不起作用。这是因为我们在效果更新时没有清除旧间隔。我们可以通过返回清理函数来解决这个问题:
¥If we then click the ‘slow down’ button... well, it doesn’t work. That’s because we’re not clearing out the old intervals when the effect updates. We can fix that by returning a cleanup function:
$effect(() => {
const id = setInterval(() => {
elapsed += 1;
}, interval);
return () => {
clearInterval(id);
};
});
当 interval
更改时,以及当组件被销毁时,在效果函数重新运行之前立即调用清理函数。
¥The cleanup function is called immediately before the effect function re-runs when interval
changes, and also when the component is destroyed.
如果效果函数在运行时没有读取任何状态,它将只在组件挂载时运行一次。
¥If the effect function doesn’t read any state when it runs, it will only run once, when the component mounts.
服务器端渲染期间不会运行效果。
<script>
let elapsed = $state(0);
let interval = $state(1000);
</script>
<button onclick={() => interval /= 2}>speed up</button>
<button onclick={() => interval *= 2}>slow down</button>
<p>elapsed: {elapsed}</p>