$state、$derived、$effect
Svelte 5 的 runes 把“可写状态”“纯派生值”“外部副作用”分开。$derived 应由输入计算出结果;$effect 用于同步 DOM、网络或第三方库,不能把派生值再写回 state 形成循环。
最小可运行组件
svelte
<script>
let count = $state(0)
let doubled = $derived(count * 2)
let input
$effect(() => {
document.title = `count=${count}`
return () => { document.title = 'Svelte demo' }
})
</script>
<button onclick={() => count++}>count: {count}</button>
<p bind:this={input}>double: {doubled}</p>点击按钮后只需观察 count、doubled 和页面标题:派生值同步更新,effect 在组件销毁时执行清理。把 doubled 改为在 effect 中 doubled = count * 2 会引入额外写入,甚至形成更新循环。
更新追踪与异步边界
rune 表达式只追踪本次运行实际读取的状态。异步回调中捕获的值可能已经过期,因此请求要保存序号或 AbortController;effect 清理函数先取消旧请求,再提交新结果。服务端渲染阶段没有 window,浏览器 API 必须放进 effect 或 onMount 等客户端边界。
选择与排错
- 纯计算用
$derived,不要为它再建一个可写 state。 - 订阅、计时器、DOM API 和第三方实例用
$effect,并返回对称清理。 - 需要跨组件共享时使用 store;不要把模块级可变变量当成隐式全局状态。
练习
- 将搜索框示例改为请求取消:新关键词到来时取消旧请求,并显示 loading/error/empty 三态。
- 删除一个 effect 写回,改成
$derived,比较更新次数和结果。 - 在组件销毁后检查定时器、订阅和 document.title 是否恢复。