:Preferences结合AppStorage实现收藏和历史)
文章导读Preferences 负责落盘AppStorage 负责页面响应式镜像。数组修改必须先 slice 复制再 setOrCreate 写回避免 UI 感知不到变更。收藏、关注、浏览历史和浏览量都通过 PrefStore 统一维护。页面效果收藏、关注、历史和浏览量应在页面切换后保持一致重启应用后仍能恢复用户不会感觉数据丢失。实战拆解本地状态是移动应用绕不开的部分。羽球联盟没有把 Preferences 调用散落到各页面而是用 PrefStore 统一管理。页面只调用 toggleFavorite、toggleFollow、addHistory、login、logout 等方法持久化细节藏在 store 内部。这篇文章最重要的技术点是AppStorage.get 返回的数组不能原地 splice、push、unshift 后就期待 UI 刷新。正确做法是先 slice 得到副本修改副本再 AppStorage.setOrCreate 写回。项目里的 toggle 和 addHistory 都遵循这个模式。Preferences 与 AppStorage 的关系可以解释成“磁盘真相 内存响应式镜像”。启动时hydrate()把磁盘数据恢复到 AppStorage用户操作时先更新 AppStorage 保证 UI 立即变化再写 Preferences 持久化。这样体验和可靠性都比较平衡。HarmonyOS 官方文档里Preferences 适合保存轻量级键值数据而 AppStorage 适合在页面之间共享响应式状态。把两个能力拆开用既能保留 ArkUI 的联动体验也不会让页面层直接承担序列化和落盘细节。这里可以对照官方 API 说明Preferences 与 AppStorage。项目里还有一个很实用的细节PrefStore会先根据登录态切换scope再把favorites、history、follows和viewCountsJson重新加载到 AppStorage。这样游客态和登录态看到的是不同数据空间个人中心的统计数字也能跟随账号切换即时刷新。关键代码private async toggle(key: string, id: string): Promiseboolean { const original: string[] AppStorage.getstring[](key) ?? []; const next: string[] original.slice(); const idx: number next.indexOf(id); const added: boolean idx 0; if (added) { next.unshift(id); } else { next.splice(idx, 1); } AppStorage.setOrCreatestring[](key, next); await this.persist(key, next); return added; } async addHistory(id: string): Promisevoid { const original: string[] AppStorage.getstring[](history) ?? []; const next: string[] original.slice(); const idx: number next.indexOf(id); if (idx 0) { next.splice(idx, 1); } next.unshift(id); if (next.length 100) { next.length 100; } AppStorage.setOrCreatestring[](history, next); await this.persist(history, next); await this.incrementView(id); }这段实现把“收藏开关”和“浏览历史去重 头插 截断”放在同一个 store 中处理。页面只感知结果真正负责一致性的还是slice - 修改副本 - setOrCreate - persist这一条链路。取舍分析这里的核心取舍不是“要不要持久化”而是“让谁负责持久化”。如果每个页面都各自调用 Preferences短期写起来很快但收藏、历史和统计数字迟早会出现不同步。把更新入口集中到PrefStore后页面层只负责触发动作状态边界会清晰很多。另一个取舍是性能和体验的平衡。这里没有在每次点击时重新hydrate()全量数据而是只更新当前受影响的 AppStorage 键然后异步落盘。对用户来说收藏按钮和历史列表会立即变化对工程来说Preferences 仍然保存着重启后可恢复的最终结果。设计落点Preferences 负责持久化AppStorage 负责页面响应式两者职责不同。数组状态需要复制后写回不能指望原地修改触发所有视图刷新。统一 store 能避免多个页面各自读写造成状态竞争。历史记录在写入时顺手维护浏览量可以少做一次额外的页面埋点同步。易踩坑不要在多个页面直接操作同一个 Preferences 文件状态会难以同步。不要直接持久化 Resource 或 class 实例保存纯 JSON 更稳。不要把AppStorage.get()拿到的数组当成普通局部变量原地改动StorageLink很可能收不到刷新信号。验证方式收藏后重启应用确认状态仍在。清空历史后确认个人中心统计同步归零。连续切换页面确认数字不会延迟刷新。游客态添加收藏后再登录其他账号确认两个数据空间不会互相覆盖。小结Preferences 结合 AppStorage 的价值不只是“把数据存下来”而是把页面响应、账号隔离和持久化入口统一到一个地方。对羽球联盟这样的内容型应用来说这种组织方式能明显降低收藏、历史和统计状态失真的概率也更适合作为后续设置项、草稿箱或离线缓存能力的基础。