Store 与共享状态
store 适合跨组件客户端状态;服务端数据需要 load/cache/invalidation。练习:登出清理旧用户 store。
核心机制与实现
可写 store 通过 subscribe 暴露当前值和退订函数;组件销毁时必须调用退订。把服务端列表放进 store 前先定义身份和失效,否则登出后旧用户数据会继续被新页面读取。
ts
import { writable, derived } from 'svelte/store'
export const cart = writable<{ id: string; quantity: number }[]>([])
export const total = derived(cart, items => items.reduce((sum, item) => sum + item.quantity, 0))
export function add(id: string) {
cart.update(items => items.some(item => item.id === id)
? items.map(item => item.id === id ? { ...item, quantity: item.quantity + 1 } : item)
: [...items, { id, quantity: 1 }])
}测试 add('book') 两次应得到一个数量为 2 的条目,total 为 2;调用订阅返回的 stop() 后再更新,不应再收到通知。Svelte 5 Runes 可在组件内表达相同依赖,但跨模块共享仍要明确 store 的生命周期和清理责任。
本节专属实验:区分派生值和副作用
在 Svelte 5 组件中记录 $state、$derived 和 $effect 的执行顺序。先让派生值只由输入计算,再把同样逻辑错误地写成 effect 写回 state,比较更新次数并观察循环风险。
svelte
<script>
let count = $state(0)
let doubled = $derived(count * 2)
$effect(() => console.log('external sync', count))
</script>
<button onclick={() => count++}>{count} / {doubled}</button>预期:点击只改变事实状态,派生值同步变化;组件销毁时 effect 的清理函数执行一次。
可运行实验
- 修改状态、派生值和数组项,观察哪些更新会触发。
- 将组件销毁,确认订阅、定时器和事件监听是否清理。
- 说明该概念与 SSR、stores、actions 或过渡动画的连接。
实验记录至少包含:环境版本、输入序列、预期结果、实际结果、失败截图或日志,以及修复后再次运行的结果。
练习
- 用自己的话画出本篇概念的依赖图,并标出一个可能产生重复执行或竞态的节点。
- 写一个最小测试覆盖成功、失败、取消/卸载三条路径;测试应断言可观察结果,而不是内部实现调用次数。
- 给出一个选择当前方案而不是替代方案的理由,并写下替代方案何时更合适。
下一步
完成练习后阅读本章答案。