【寻迹校园 HarmonyOS NEXT 实战 07】不引入全局状态库:用 dataRevision 实现跨页面刷新
【寻迹校园 HarmonyOS NEXT 实战 07】不引入全局状态库用 dataRevision 实现跨页面刷新这是“寻迹校园 HarmonyOS NEXT 实战”系列第 7 篇。本文拆解Index.ets的dataRevision方案Service 和 Repository 保存权威数据页面只传递失效信号让首页、消息和“我的发布”在写入后重新读取而不是维护多份可变副本。上图为本文原创生成的状态刷新概念图不是项目截图。中心是 Service/Repository 权威数据周围页面只监听版本变化并重新读取。一、跨页面刷新最容易犯什么错一个失物招领记录会同时影响多个页面发布成功后首页列表要出现新记录提交认领后消息页要出现待处理信息撤回或删除后“我的发布”要更新双方确认交接后详情状态和统计数量都要变化举报成立后被隐藏记录不应继续出现在首页。最直接的做法是在回调里手工修改多个数组和计数例如给首页列表push()一条记录、给个人中心数量加一、给消息角标加一。问题是这些副本没有统一事务边界一次写入失败、一次返回路径遗漏或一次重复回调都可能让页面状态和数据库不一致。项目选择更简单的规则写入只发生在 Service/Repository页面收到成功结果后只递增一个版本号所有消费者再从权威数据源读取。二、dataRevision 是失效信号不是业务数据Index.ets中只有一个轻量状态LocaldataRevision:number0;它不表示记录数量、消息数量或数据库版本也不会被持久化。它只表达一句话“权威数据可能已经变化请重新加载。”这和缓存失效的思想相同。消费者不需要知道发生了发布、认领还是举报只要发现dataRevision改变就重新调用对应 Service。这种设计有两个好处页面之间不传递整份可变列表写入方不需要知道有哪些页面正在展示这些数据。三、初始化完成后为什么也要递增一次应用启动时Repository 可能需要初始化 RelationalStore、种子数据和本地回退。页面第一次构建得比异步初始化更快就可能先显示空态或回退数据。项目在业务初始化全部完成后递增一次版本privateasyncinitializeApplication():Promisevoid{constcontext:common.UIAbilityContext|undefinedthis.getAbilityContext();if(!context)return;awaitthis.initializeBusiness(context);this.dataRevision;}privateasyncinitializeBusiness(context:common.UIAbilityContext):Promisevoid{awaitreportService.initialize(context);awaitclaimService.initialize(context);awaithandoffService.initialize(context);awaitmoderationService.initialize(context);settingsService.initialize(context);}这一步很关键它把“数据层准备完成”也当成一次失效事件通知已渲染页面重新读取避免首页长期停留在初始化前结果。四、子页面只在业务成功后触发 onDataChanged页面导航由Index统一组装。发布、认领、交接和举报页面都接收一个回调PublishFormPage({pathStack:this.pathStack,reportType:ReportType.LOST,onPublished:()this.dataRevision})ClaimReviewPage({pathStack:this.pathStack,claimId:claimId,onDataChanged:()this.dataRevision})子页面不直接访问Index的列表也不返回一份修改后的全局对象。它只在 Service 返回成功之后调用回调。例如发布页的顺序是校验草稿、调用createReport()、确认OperationResult成功、触发onPublished()、进入成功页。如果保存失败版本不递增其他页面也不会被无意义刷新。上图展示完整时序页面发起动作Service 写入权威数据成功后 Shell 递增版本多个消费者分别重新查询。版本号从不承载业务内容。五、消费者应该如何响应版本变化首页、消息页和“我的发布”把dataRevision作为输入参数。组件在首次出现或参数变化时触发加载最终数据仍来自 Service。逻辑上可以概括为dataRevision 变化 - 页面将 loading 设为 true - 调用对应 Service 查询 - 将结果映射成 UI-ready 状态 - 处理 empty / error / success - loading 结束这里最重要的不是某个生命周期 API而是数据所有权版本变化只能促使页面“重新读”不能把页面当前数组当成数据库继续修改。六、为什么不直接使用 AppStorage 保存列表AppStorage适合轻量跨页面状态或刷新信号但不适合存放完整的权威业务记录。把报告列表、认领记录和治理案件同时塞进全局存储会带来页面可以绕过 Service 任意修改数据全局对象与 RelationalStore 出现双重真相大列表更新扩大重渲染范围生命周期和持久化边界变得模糊测试难以判断数据来自数据库还是上一次页面残留。dataRevision的优点正是它足够“小”只通知失效不复制业务数据。七、错误的“手工改多个计数”为什么危险假设一次认领提交需要同时执行新增 Claim、把 Report 从OPEN改为CLAIMING、刷新消息、更新个人中心统计。如果页面自己执行messageCount、claimingCount、替换列表项至少存在四种风险Service 实际写入失败但计数已经增加用户快速点击导致回调执行两次其他页面没有挂载错过这次局部更新状态机后续回滚旧计数没有恢复。重新从 canonical data 聚合统计虽然多一次读取却换来了可验证的一致性。对于当前单机数据量这个成本远小于维护多份补丁状态。八、如何防止刷新风暴失效信号简单但仍需要约束只在业务动作真正成功后递增一个完整事务只发一次信号页面加载方法要能处理快速连续调用异步结果回写前确认页面仍需要该结果不在build()中发起持久化查询loading、empty 和 error 必须稳定不因刷新闪烁布局大数据场景要增加缓存、去抖或请求 ID而不是无限全量查询。在更复杂页面中可以为加载请求增加递增 ID只接受最后一次响应防止旧请求覆盖新状态。九、什么时候该升级为 ViewModel 或事件总线dataRevision适合当前单进程、单机、页面数量有限的项目但它不是万能全局状态方案。出现以下情况时应升级多个页面需要共享同一份复杂 UI-ready 状态列表规模大任何变更都全量重查成本过高写入事件需要携带类型化增量信息需要撤销、乐观更新或离线冲突合并多窗口、多设备或远端推送同时修改数据页面逻辑已经难以在组件内部测试。升级方向可以是明确的 ViewModel、Repository 观察流或类型化事件总线。无论采用哪种方式Service/Repository 仍应持有权威业务数据。十、它不解决跨设备同步dataRevision只存在当前进程内。应用被关闭后它会重置也不会通知另一台设备更不具备服务端版本、冲突检测和增量同步能力。因此正确表述是它解决当前单机 HarmonyOS 应用的跨页面刷新不是云同步、分布式数据或多账号实时协作方案。十一、验收清单本项目用以下场景验证刷新链初始化完成后首页从 Service 重新读取发布成功返回首页出现新记录编辑、撤回和删除后“我的发布”更新认领提交后目标记录进入处理中审核、交接和双方确认后相关页面状态一致举报隐藏后首页不再展示目标记录写入失败时版本不递增页面显示统一错误。这些场景验证的是状态流而不是只检查某个按钮能否点击。十二、本文小结dataRevision是一个低成本的失效信号Service/Repository 负责写入 canonical data子页在成功后递增版本首页、消息和个人中心重新读取自己的权威数据。它避免了多页面手工拼补数组和计数也保留了未来升级 ViewModel 或事件流的空间。下一篇将从交付风险出发说明为什么核心应用与小艺 Agent 要拆成 V1.0 和 V1.1 两条可独立验收的路线。系列导航第 7 篇 / 共 50 篇。上一篇《共享权威词表设计》下一篇《V1.0 核心应用 V1.1 小艺增强》。