
一、最高频的写操作最不能丢的数据菜谱应用里有一类数据的特点是小、碎、频收藏一条菜谱、记下看过的菜、标记本周做过——单条不到 100 字节但每次交互都可能触发写盘而且是用户最不能接受丢失的数据收藏丢了会直接投诉。1.0.4 起这类数据由一个 UserDataManager 统一管理本文复盘它的三层流转设计和四个关键取舍。二、三层架构UI 订阅、运行时聚合、磁盘兜底最底层是 Preferencesuser_data 存储上面是 AppStorage 作为运行时单一事实源UI 组件全部通过 StorageLink 订阅。任何页面改收藏走的是同一条路操作函数返回新数组 → 写 AppStorage → 同步序列化进 Preferences。没有页面直接碰 Preferences也没有页面自己维护副本——副本一旦出现同步 bug 只是时间问题。启动时的 init 用 getPreferencesSync 一次性把五类记录读进 AppStorage同步加载保证首帧就有数据这是启动可容忍几十毫秒换来的简单性。三、取舍一记录数组 单键 JSON而不是关系库每类行为数据是一个带时间戳的记录数组recipeId createdAt整体 JSON.stringify 后存进一个 Preferences 键。为什么不上 RDB数据量小收藏几十条封顶、结构稳定、查询就是内存里的 find/filter。关系库的正确引入时机是需要条件查询和分页——目前一条都不需要。诚实说这个方案的天花板JSON 大字符串意味着每次收藏都全量重写整个数组几百条无感几千条会有明显的写放大到那个量级再迁 RDB接口不用变persist 是唯一出口。四、取舍二不可变更新是硬约束toggleFavorite 的写法值得单独说statictoggleFavorite(records:FavoriteRecord[],recipeId:number):FavoriteRecord[]{constindexrecords.findIndex(rr.recipeIdrecipeId)if(index0){constnewRecords[...records]newRecords.splice(index,1)returnnewRecords}return[{recipeId,createdAt:dateStr},...records]}取消收藏时先复制再 splice最后返回的是新数组——不是改完原数组返回它。这是 ArkTS 状态管理 V1 的硬约束StorageLink 靠引用变化感知更新原地 mutate 一个数组UI 就不动了。所有七个操作函数都遵守这个约定这一致性比任何单个函数的实现都重要。五、取舍三MRU 语义压缩在一个函数里浏览历史的 addHistory 干了三件事filter 掉同 id 旧记录、把新记录插到最前、带上时间戳。一次调用完成去重置顶计时页面侧只需要records addHistory(records, id)一行。历史天然是最近优先MRU结构不需要排序逻辑——插入位置就是排序。清空历史同样只有几行置空数组 persist。六、取舍四同步 API 的边界putSync 加 flush 的写路径在这个量级完全够用单条记录百字节级全量序列化也就几 KB磁盘操作毫秒级完成。但同步意味着在 UI 线程做 IO这个设计成立的前提是数据量小。我已经在 persist 里留了唯一的出口——将来换异步 API 或 RDB改这一层七个操作函数和所有页面无感。七、结语用户行为数据的设计要点不在存储选型在流转纪律单一事实源、不可变更新、写路径收敛到一个函数。做到这三条数据小的时候跑得轻快数据大了换存储也不伤筋动骨。参考资料Preferences 文档kit.ArkData工程源码UserDataManager.ets、FavoritePage.ets#鸿蒙 #HarmonyOS 7API 26