12 KiB
12 KiB
前端导航解耦与统一缓存重构设计
1. 背景与问题定义
当前 apps 端在日历(日/月)与待办页面中存在以下系统性问题:
- 页面切换语义错误:将业务 tab 切换实现为
push/go混用,导致页面重建与路由栈膨胀。 - 数据刷新触发错误:页面通过路由监听触发
load,频繁重复请求后端。 - 状态职责耦合:导航状态、页面状态、数据状态边界不清,导致“切换逻辑改动会牵出数据 bug”。
- 回主页语义不一致:Dock 首页按钮被
canPop -> pop策略污染,行为变成“返回上页”。 - 缓存能力分散:仅存在局部的个人信息缓存(
SettingsUserCache),缺少统一可复用缓存模块。
目标是完成一次结构化重构,建立「解耦的导航切换 + 统一缓存 + 可控一致性」体系。
2. 目标与非目标
2.1 目标
- Home/Calendar/Todo 切换不重建主页面(保持页面实例与滚动状态)。
- 日/月视图切换不触发整页重建和无必要网络请求。
- 建立统一缓存模块,合并个人信息缓存并覆盖 Calendar/Todo 数据读取。
- 启动体验采用「本地优先 + 后台静默刷新」策略,减少进入 App 的重复请求。
- 数据只在必要时刷新:手动下拉、写操作失效、生命周期关键点、缓存策略命中。
- 主页按钮语义固定为“回主页”,不再变成“返回上一页”。
- 一级页面唯一为 Home,日历日/月视图、待办、设置均为二级页面;二级页面侧滑只允许返回一级页面,不允许直接退出 App。
- App 退出入口仅存在于一级页面(Home)。
2.2 非目标
- 本次不改后端协议与接口契约。
- 本次不引入复杂离线同步冲突解决(如多端 CRDT)。
- 本次不引入全量本地数据库迁移(先基于 SharedPreferences 持久化层)。
3. 复杂度与风险分级
- Complexity:
S3- 跨 router、calendar、todo、settings、DI 的架构级调整。
- Risk Tier:
L1- 不触及鉴权协议和支付等高风险域,但涉及导航返回栈与数据一致性高回归区。
4. 架构总览
4.1 导航分层
采用分级导航:
- 一级页面(唯一):Home
- 仅 Home 允许触发系统退出路径。
- 二级页面(主业务入口)
- Calendar Day/Month
- Todo List(Quadrants)
- Settings
- 规则:二级页面的系统返回/侧滑返回统一回 Home,不允许直接退出 App。
- 三级页面(细节页)
- Calendar event detail/edit/share
- Todo detail/edit
- Settings 子页面(account/profile edit 等)
- 规则:三级页面返回到上一级(二级或三级上层)。
4.2 状态与数据边界
- 导航状态:Shell 当前分支 index、Calendar 内部视图类型。
- UI 状态:选中日期、滚动位置、拖拽态、loading/error。
- 数据状态:统一缓存模块管理(内存 + 持久化 + 网络回写)。
结论:页面只发“意图”,不直接承担缓存与路由策略。
5. 统一缓存模块设计
5.1 模块结构
新增 apps/lib/core/cache/:
cache_key.dart- 统一 key 命名规范。
cache_policy.dart- TTL、软/硬过期、最小刷新间隔、刷新原因枚举。
cache_entry.dart- 标准缓存实体(data/fetchedAt/expiresAt/version/dirty)。
cache_store.dart- 抽象接口(get/set/remove/invalidateNamespace)。
memory_cache_store.dart- 会话级热缓存。
persistent_cache_store.dart- 本地冷缓存(SharedPreferences JSON)。
hybrid_cache_store.dart- 两级缓存协调与 singleflight 去重。
cache_invalidator.dart- 统一精准失效入口。
5.2 key 设计(首版)
- 用户信息
user:profile:me
- 日历
calendar:day:YYYY-MM-DDcalendar:month:YYYY-MM
- 待办
todo:list:pendingtodo:list:priority:<n>(按需)todo:detail:<id>(按需)
5.3 策略设计(平衡型)
读取顺序:memory -> persistent -> network。
刷新策略:
- 软过期(stale-while-revalidate)
- 先展示缓存,后台静默刷新。
- 硬过期
- 超过硬过期后必须请求网络或提示数据过旧。
- 最小刷新间隔
- 避免频繁切换/回前台引发抖动请求。
建议默认值:
user:profile:软过期 30min,硬过期 24h。calendar:day:软过期 2min,硬过期 30min。calendar:month:软过期 5min,硬过期 60min。todo:list:pending:软过期 2min,硬过期 30min。
5.4 个人信息缓存合并方案
现有 SettingsUserCache 并入统一缓存模块:
- 新建
UserProfileRepository(或在现有 settings service 中引入统一缓存)。 getProfile()通过 hybrid cache 获取user:profile:me。- 更新 profile 成功后立即写回缓存并同步持久化。
- 登出/会话失效时统一调用
invalidateNamespace('user')。
6. 一致性风险与解决方案
平衡型缓存会存在“短暂陈旧窗口”。本设计通过以下机制将体验风险降到可接受范围。
6.1 触发刷新矩阵
- 手动下拉刷新:强制网络刷新。
- 写操作成功:精准失效受影响 key 并触发回填。
- App 回前台:若超过最小刷新间隔,触发静默刷新。
- 网络离线 -> 在线:触发静默刷新。
- 进入关键详情页:按策略进行 freshness check。
6.2 写后一致性
- 乐观更新:本地先更新 UI 与缓存,避免“我刚改完却没变”。
- 失败回滚:API 失败时恢复旧值并 Toast 提示。
- 精准失效:不做全局清空,只失效关联 key,兼顾一致性与性能。
6.3 并发安全
- singleflight:同 key 同时只允许一个网络请求。
- 版本保护:缓存写入比较
updatedAt/version,拒绝旧响应覆盖新状态。 - 失败兜底:请求失败不清空旧缓存,保持可读并允许重试。
6.4 可见性保障
- 页面可显示“上次同步时间”(轻提示)。
- 硬过期数据需可见提醒(弱提示,不阻断基础浏览)。
- 提供稳定手动刷新入口。
6.5 日历提醒取消动作的一致性兜底
- 用户在提醒弹层点击“取消/归档”时,前端必须立即发送归档请求,要求后端立刻将事件归档/过期。
- “延迟归档(outbox/pending)”仅在 App 进程不可用(被杀/未启动)时生效,作为离线或冷启动兜底。
- App 冷启动或恢复前台后,必须优先冲刷 pending 归档请求,确保最终一致性。
- 对用户可见行为要求:点击取消后 UI 立即反映归档状态,网络失败时展示重试提示,并保留 pending 记录。
7. 导航与页面职责重构
7.1 路由重构
app_router引入 shell 分支,不再平铺所有主页面。- Dock 切换改为 branch index 切换,不再
push主页面。 - Calendar 内部 month/day 切换改为视图切换,不新增栈层。
- 事件详情/编辑等保留
push(细节页合理叠栈)。
7.2 回主页逻辑修正
- Dock Home 统一执行“切到 Home 分支/
go('/home')”。 returnToHomePreserveState仅用于非 Dock 的返回策略场景。- 消除
canPop -> pop对主页按钮语义的影响。 - 二级页面(Calendar Day/Month、Todo、Settings)统一拦截系统返回和侧滑返回,目标固定为 Home。
- App 退出只允许在 Home 页面生效(可采用双击退出或系统默认行为)。
7.3 页面职责收敛
- Calendar/Todo 页面移除路由监听触发
load。 - 页面只调用 repository:
get(policy)refresh(force: true)mutate(...) + invalidate(...)
- 页面不直接感知“缓存在哪一层”。
8. 分阶段实施计划(里程碑)
M1 导航壳层与切换语义
- 引入 shell + 分支保活。
- Dock 接口改造与主 tab 切换实现。
- Home 按钮语义修正。
- 建立分级返回约束:二级 -> Home,三级 -> 上一级,退出仅 Home。
M2 统一缓存骨架
- 新增 core cache 模块。
- 接入 user profile(替换
SettingsUserCache)。 - DI 注入 cache store 与 invalidator。
M3 Calendar 接入
- 引入
CalendarRepository与 day/month key。 - 移除 route listener 自动刷新。
- 切换 month/day 时默认走缓存,不触发无必要请求。
M4 Todo 接入
- 引入
TodoRepository与 list/detail key。 - 拖拽、完成、编辑后的精准失效。
- 下拉刷新走强制网络。
M5 清理与验证
- 清理旧缓存与重复加载逻辑。
- 补齐测试与性能观测。
- 评估参数并收敛默认策略。
- 验证提醒“点击取消即实时归档”与“App 关闭时延迟归档兜底”双路径。
9. 验收标准
9.1 体验验收
- Home/Calendar/Todo 切换无明显重建卡顿。
- 日/月切换响应明显变快。
- 首次冷启动可先看到本地缓存内容。
- Dock Home 始终回主页。
- 二级页面侧滑返回永远回 Home,不直接退出 App。
9.2 网络验收
- 切换页面时网络请求显著减少。
- 写操作后关联数据可及时更新。
- 手动刷新可强制拉取并回写缓存。
- 提醒取消动作触发实时归档请求,成功率可观测。
9.3 一致性验收
- 不出现旧响应覆盖新数据。
- 离线后恢复在线可自动静默同步。
- 软过期/硬过期行为符合策略定义。
- 提醒归档在在线/离线/冷启动场景下保持最终一致。
10. 测试与验证计划
10.1 单元测试
hybrid_cache_store:命中链路、singleflight、软硬过期判定。cache_invalidator:写操作触发的 key 精准失效。- repository:读缓存、后台刷新、失败兜底、版本保护。
10.2 组件/页面测试(高回归)
- Dock 切换不重建分支主页面。
- 日/月切换不重复触发全量加载。
- Home 按钮行为稳定。
- 二级页面系统返回不会触发 App 退出。
10.3 集成回归
- Calendar -> Todo -> Calendar 多轮切换请求计数。
- Todo 完成后列表更新与缓存一致性。
- profile 更新后设置页/其他依赖页可见一致。
- 提醒取消 -> 立即归档 -> 日历列表刷新链路。
- App 杀进程后触发提醒,重启后 pending 归档自动冲刷。
11. 风险与回滚
11.1 主要风险
- 导航壳层改造可能引发深链与返回栈回归。
- 缓存策略参数不当可能造成陈旧感。
- 早期失效 key 设计不完整可能出现局部不刷新。
11.2 控制策略
- 按里程碑逐步落地,每个里程碑可单独回滚。
- 默认保留手动刷新兜底。
- 增加请求计数与缓存命中日志(开发态)。
11.3 回滚策略
- 若 M1 不稳定,可先回退 shell 改造并保留缓存模块。
- 若缓存接入问题集中,可按域回退(user/calendar/todo 分域开关)。
12. 最终落地参数(2026-03-20)
- 导航分级
- 一级页面唯一为
Home。 - 二级页面(日/月、待办、设置)侧滑返回统一回
Home,不允许直接退出 App。 - App 退出入口仅保留在
Home。
- 一级页面唯一为
- 缓存默认策略
user:profile:软过期 30min,硬过期 24h。calendar:day:软过期 2min,硬过期 30min。calendar:month:软过期 5min,硬过期 60min。todo:list:pending:软过期 2min,硬过期 30min。
- 生命周期刷新
- App 回前台时启用最小间隔 5min 的静默刷新协调器。
- 提醒归档策略
- App 活跃态点击取消:立即请求后端归档。
- 延迟归档(pending/outbox)仅用于 App 不可用场景兜底。