哪些状态可以保存
持久化是生命周期选择,不是“加一个插件”。主题、语言和关闭提示可以写入本地存储;访问令牌、权限结果和服务端秘密不应因为方便而放进可被脚本读取的状态。
ts
function readTheme(): 'light' | 'dark' {
const value = localStorage.getItem('theme');
return value === 'dark' ? 'dark' : 'light';
}本地存储可能被用户手工修改、被其他标签页改变,也可能在隐私模式或配额不足时失败。读取必须校验,写入必须允许失败,登出必须清理与账号绑定的数据。
练习
为主题偏好设计持久化策略,并列出登出时需要清理的状态。明确哪些数据只能从服务端重新获取,哪些数据即使泄露也不会造成权限提升。
核心机制与实现
状态设计先区分本地 UI、跨组件共享、服务器缓存和 URL 状态;全局 store 不是默认答案。
围绕“哪些状态可以保存”先写出输入 → 中间状态 → 可观察结果,再实现最小闭环。不要把事件监听、异步请求、DOM 读取或 store 写入混在一个函数里;每个副作用都要有创建、完成、取消和清理路径。
js
type State = { filter: string; selectedId: string | null }
const initial: State = { filter: '', selectedId: null }
function reducer(state: State, action: Action): State {
if (action.type === 'filter/changed') return { ...state, filter: action.value }
return state
}逐步运行:先只验证成功路径,再改变一个输入;记录时间顺序、对象身份、错误类型和最终可见状态。若环境不支持某个浏览器 API,要把静态协议验证和真实浏览器验证分开记录。
失败边界与变体
- 输入为空、重复、过期或非法时,不应静默覆盖有效状态。
- 组件卸载、路由离开、标签页关闭或请求取消时,资源必须停止写入。
- 跨文档、Shadow DOM、Worker、SSR 或多标签页会改变可见边界,不能沿用单页面直觉。
- 需要持久化时记录 schema 版本和迁移失败策略;需要全局状态时先证明局部状态不足。
可运行实验与练习
画出状态所有者、读者和写入者;测试刷新、回退、并发请求、错误恢复和持久化迁移,解释为何某个状态不应放进全局 store。
练习要求:实现一个最小版本,补一个失败测试,再给出一个替代方案及其适用条件。答案应包括实际输入序列、观察结果、修复原因和未覆盖的浏览器差异。