页面、路由和状态应该放在哪里
按寿命划分 URL、局部和远程状态,再为阅读清单设计刷新路由、功能边界与可恢复的实时更新。
实现可分享与可恢复的清单状态,并验证后退、刷新、重复更新和版本缺口。
阅读清单页把所有信息放进一个全局 store:搜索词、登录身份、弹窗、服务器结果和连接状态互相引用。刷新页面后筛选丢失,后退按钮不恢复列表,旧消息还会覆盖新数据。问题通常在状态的归属和恢复规则,增加一个管理库未必能解决。
案例是一页可分享的阅读清单,支持分类、排序、收藏和后台任务进度。先按寿命与共享方式分类,再设计路由、功能模块与更新协议。离线示例只验证版本拒绝规则,不建立真实实时连接。
按状态寿命确定保存位置
搜索词、分类、排序和可分享的页码适合 URL,别人打开链接可以重建同一视图。弹窗开关、输入焦点和未提交输入通常属于局部界面状态。服务器记录由后端负责,前端缓存只是副本;登录身份还需要明确会话与权限边界。
不要把服务器记录复制到多个互不关联的 store,再让各页面独立更新。为每个数据指定权威来源和同步路径。远程查询结果需要缓存键、失效规则、加载与错误状态,局部弹窗无需承担这些职责。
URL 参数来自外部,必须解析和校验。页码限制范围,排序字段只允许已知值,分类不存在时给出明确回退或错误。不能因为参数出现在自己的页面地址中,就信任它能直接拼进数据库或渲染 HTML。
路由同时涉及浏览器与服务器
History API 可以增加或替换历史条目;浏览器后退、前进引发的恢复需要处理相应 popstate。应用自己修改历史后,也要按路由机制更新视图,不能假设每次 pushState 都会自动触发同样的事件。
分享链接与刷新要求服务器或静态托管能够返回对应路径。开发服务器显示页面,不代表生产托管已支持这些 URL。路径导航到资源文件时,也不能把所有请求都回退到同一个 HTML,否则缺失图片会返回页面文本。
筛选改变通常需要决定重置页码还是保留定位;后退需要恢复参数与视图。把这些行为写进路由契约,再测试从入口点击、直接打开和刷新三种路径。URL 与状态若互相无限写入,会造成重复导航,应只在必要变化时同步。
功能模块通过窄接口协作
阅读清单模块可以拥有查询、列表和收藏操作,任务模块拥有进度与重试,会话模块提供当前身份。模块暴露必要的读取与操作接口,避免页面跨越边界改动另一个模块的内部对象。
一个 store 可以实现这些边界,但不能凭“统一状态”消除所有责任。也可以用局部状态、查询缓存和少量共享上下文组合。按项目变化选择,不为了架构图把一个简单页面拆成大量互相转发的层。
操作状态需要合法转换,例如 queued 到 running,再到 succeeded 或 failed。取消与重试的语义独立:取消可能只停止界面等待,真实任务是否停止由服务端确认。前端按钮不能直接宣布后台工作已经取消。
选择轮询、SSE 或 WebSocket
轮询按间隔请求最新状态,实现简单,但频率越高越增加请求负载。需要明确停止条件、页面不可见时的策略以及重试预算。任务结束后应停止无意义轮询,而不是永远重复查询。
SSE 通过 HTTP 提供服务器到客户端的事件流,适合单向进度通知;WebSocket 提供双向消息通道,适合需要双方频繁通信的场景。选择还取决于代理、认证方式、连接生命周期与平台能力,不能只按“实时”二字决定。
无论哪种通道,应用仍要设计重连、重复消息、状态恢复和背压。WebSocket 能收发消息,不等于业务变化恰好执行一次。连接断开后可以获取最新快照,再按版本继续处理,避免用遗漏事件构造一个永久错误状态。
用版本检查拒绝旧更新
假设服务器为完整状态快照分配递增版本。只接受大于当前版本的快照,可以防止重复和较旧快照覆盖现值。下面是合成规则,version=2 在 version=1 前到达,随后旧版本和重复版本都被拒绝。
state = {"version": 0, "value": "initial"}
def apply(version, value):
if version <= state["version"]:
return False
state.update(version=version, value=value)
return True
assert apply(2, "new")
assert not apply(1, "old")
assert not apply(2, "duplicate")
assert state["value"] == "new"
print(state)
输出为 {'version': 2, 'value': 'new'}。代码允许从 0 跳到 2,因为输入被当作完整快照。若消息表示“给计数加一”这样的增量,跳过版本 1 就可能丢失变化,需要检测缺口并重新同步,不能直接复用这个策略。
版本只在约定范围内比较。不同资料、租户或连接时代的编号不能随意混用。重连后要知道版本来源是否保持连续,也要处理服务器重置。该函数没有线程、网络和持久化,不能证明真实实时通道可靠。
构建工具不包办应用架构
Vite 提供开发服务和构建能力,但不会自动定义权限、生产路由或服务器数据一致性。构建成功需要与托管路径、资源前缀和缓存策略对应,才能在实际部署中正确打开页面。
排查刷新失败时先看托管路由与响应类型;排查列表退回旧值时先看 URL 还原、缓存键和更新版本。把所有问题归为框架故障会错过真正边界。每项修复都需要验证直接访问、后退、重连和错误路径。
收藏操作还需要处理服务器拒绝。若先更新界面再发送请求,失败时应恢复或标记待重试,且不能让旧失败覆盖新的成功操作。对多页面共享数据,明确哪次结果使缓存失效,并检查列表和详情是否看到同一版本。局部按钮状态不能成为永久的收藏事实来源。
练习:实现可恢复的清单视图
定义 URL 参数、局部状态、远程缓存和会话信息的归属。为合成任务选择一种更新方式,写出结束、断线、重连、重复与缺口的处理。分别描述完整快照和增量消息的不同规则。
验收要求:分享与刷新保留分类和排序;后退恢复上一视图;未知参数受控处理;旧消息不覆盖新快照;增量缺口触发重新同步;任务终态停止轮询或解除无用订阅。记录真实路由与连接检查,离线版本断言只作为其中一项机制验证。
资料来源与延伸阅读
资料核验:2026-11-10。本文为原创工程讲解,教学示例不代表线上实测。