到目前为止,我们讨论的响应性主要是从状态的角度。但这只是等式的一半 — 只有某些东西对状态做出响应,状态才是响应式的,否则它只是一个普通的变量。
响应状态的东西被称为effect。其实你已经遇到过 effect 了 — 就是 Svelte 替你创建的、响应状态变化而更新 DOM,这就是一个 effect — 但你也可以使用 $effect
符文创建自己的 effect。
大多数情况下,你不应该这样做。
$effect
最好被视为一个应急方案,而不是频繁使用的东西。比如,如果你可以将副作用放在事件处理器中,那几乎总是更好的选择。
假设我们想使用 setInterval
来跟踪组件已挂载的时间。创建一个 effect:
App
<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
。
如果我们点击”减速”按钮... 好吧,它不起作用。这是因为当 effect 更新时,我们没有清除旧的定时器。我们可以通过返回一个清理函数来解决这个问题:
App
$effect(() => {
const id = setInterval(() => {
elapsed += 1;
}, interval);
return () => {
clearInterval(id);
};
});
当 interval
改变时,清理函数会在 effect 函数重新运行之前立即被调用,此外组件销毁时也会被调用。
如果 effect 函数在运行时没有读取任何状态,它将只在组件挂载时运行一次。
Effects 不会在服务器端渲染期间运行。
1
2
3
4
5
6
7
8
9
10
<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>