5.8 KiB
5.8 KiB
Wake iOS 重构与性能优化规格说明
版本: v0.1 创建时间: 2025-09-08 15:41 +08
背景与目标
- 现状问题:代码组织结构一般、页面间切换卡顿(特别是在重动画/媒体加载/网络日志时)。
- 目标:
- 提升导航一致性与可维护性(仅保留顶层 NavigationStack + Router)。
- 降低页面切换卡顿(减少主线程压力、控制刷新频率、优化媒体与动画负载、收敛网络日志)。
- 推动 Feature-Oriented 结构与 MVVM,降低视图体量与重绘范围。
架构调整总览
- 统一导航:顶层
NavigationStack(path: $router.path)(见wake/WakeApp.swift),子页面不再嵌套NavigationView;使用Router.shared.navigate/pop/popToRoot。 - MVVM:优先对
BlindBoxView引入BlindBoxViewModel,将轮询、计时器、媒体预处理、会员信息等迁至 VM。 - 并发与取消:轮询改
AsyncSequence/Task可取消;倒计时改 Combine/AsyncTimer;统一在onDisappear/路由变化处取消。 - 媒体与动画:GIF 优先替换为 Lottie 或仅在可见态播放;模糊与缩放动画范围与时机控制;媒体元数据后台计算。
- 网络日志:Debug 可控、限流;Release 关闭大段打印;使用
os_log/Logger分类。 - 工程结构:Feature-Oriented(
Core/、Features/*、SharedUI/);延续 Theme/Typography/Spacing 设计系统。
导航设计规范
- 顶层:
WakeApp中唯一NavigationStack;其它页面不使用NavigationView。 - 路由:统一通过
Router.shared.navigate(to:)、Router.shared.pop()、Router.shared.popToRoot()。 - 返回按钮:子页面通过
Router.shared.pop()而非presentationMode.dismiss()。
BlindBox 模块重构要点
BlindBoxViewModel(@MainActor):- 状态:盲盒列表/单盒数据、会员信息、计时与轮询状态、媒体 URL/尺寸/播放器句柄。
- 行为:
loadBlindBox()、start/stopPolling()、startCountdown()、prepareVideo/Image()、资源清理。
- 视图拆分:
BlindBoxHeader、BlindBoxAnimationArea(Loading/Ready/Opening/None)、BlindBoxActionButton、BlindBoxScalingOverlay。- 视图仅订阅少量
@Published,降低 body 重绘。
并发与轮询规范
- 轮询:使用
Task { for await ... in pollSequence }+task.cancel(),严禁无 cancel 的while + Task.sleep。 - 倒计时:优先 0.25s–0.5s 频率;必要时“毫秒展示”不落地 state;严格在主线程更新 UI 状态。
媒体与动画规范
- GIF -> Lottie 优先;若保留 GIF:仅在可见态播放,避免与大范围模糊+缩放并发。
- 媒体预热与尺寸探测走后台,回主线程赋值。
- 播放器生命周期集中管理,页面切换前暂停并释放。
网络日志策略
- Debug:按需与限长打印;错误优先;可通过开关关闭详细日志。
- Release:关闭大段请求/响应体打印。
工程结构规划(建议)
Core/:Utils/、Network/、Auth/、Router/、Theme/、Typography/Features/BlindBox/:View/、ViewModel/、Models/、API/、Components/Features/Subscribe/:含CreditsInfoCard、PlanCompare等Features/Memories/SharedUI/:Buttons、LottieView、SVGImage、SheetModal 等
实施计划(分阶段)
- 第一阶段(1–2 天,先解卡顿):
- 统一导航:移除子页面
NavigationView,使用顶层NavigationStack + Router。 - 计时器降频:0.25–0.5s;如非必要移除毫秒级显示。
- GIF 限制播放或替换为 Lottie;关/收敛网络大日志。
- 统一导航:移除子页面
- 第二阶段(2–4 天):
4) 为
BlindBox引入 ViewModel,迁移副作用与状态。 5) 轮询改为可取消的异步序列;媒体预热与尺寸探测后台化。 6) 视图拆分与体量控制。 - 第三阶段(持续):
7) 目录重组;ViewModel 标注
@MainActor;保留os_signpost监测关键路径。
验收标准(DoD)
- 导航:仅顶层
NavigationStack;子页面无NavigationView。 - 性能:转场掉帧率明显下降;主界面进入/退出动画流畅。
- 结构:
BlindBoxView< 300 行,主要状态/副作用位于 ViewModel。 - 资源:GIF 仅在可见时播放或替换为 Lottie;网络日志按需输出。
任务清单(同步 todo)
- nav-1 统一导航(移除子页面 NavigationView,Router 返回)
- mvvm-1 BlindBox 引入 ViewModel,迁移逻辑
- timer-1 计时器降频与取消
- polling-1 轮询可取消化
- media-1 媒体与动画优化(GIF->Lottie/可见播放)
- concurrency-1 @MainActor 与线程安全
- netlog-1 网络日志开关与限流
- structure-1 目录重组
- perf-1 性能埋点与基线
进度记录(每次执行后更新)
- 2025-09-08 15:41 +08: 创建 specs 目录与本说明(v0.1)。
- 2025-09-08 15:41 +08: 完成 nav-1(第一步):移除
wake/View/Blind/BlindOutCome.swift与wake/View/Memories/MemoriesView.swift中的NavigationView,改为使用Router.shared.pop()返回;依赖顶层NavigationStack。 - 2025-09-08 15:51 +08: 继续完成 nav-1:
- 移除
wake/View/Credits/CreditsDetailView.swift、wake/View/Welcome/SplashView.swift、wake/View/Owner/SettingsView.swift、wake/View/Components/Upload/MediaUpload.swift(示例MediaUploadExample)、wake/View/Examples/MediaDemo.swift中的NavigationView。 - 将
wake/View/Feedback.swift中FeedbackView与FeedbackDetailView的返回行为从dismiss()统一为Router.shared.pop()。 - 保留预览(Preview)中的
NavigationView,运行时代码已全部依赖顶层NavigationStack + Router。
- 移除
决策记录
- 采用顶层
NavigationStack + Router,子页面取消NavigationView。 BlindBox优先落地 MVVM 重构,其它模块随后跟进。