本篇看点
- 阅读目标:理解 Preferences 的轻量键值边界,并和 RDB 的业务列表场景区分开。
- 核心问题:Preferences 适合轻量键值,不适合承载复杂业务列表。
- 对比迁移:对应 iOS UserDefaults 和 Flutter shared_preferences。
前言
本地缓存可以先区分数据形态。主题、语言、开关、点赞状态、首次启动标记,这些轻量键值数据,用 Preferences 更合适。
iOS 里对应 UserDefaults,Flutter 里常用 shared_preferences。HarmonyOS 的 Preferences 也是这类能力:轻量、键值、适合小数据。
一. Preferences 适合什么
适合:
- 暗黑模式选择。
- 多语言选择。
- 用户设置开关。
- 文章点赞 id 集合。
- 搜索历史少量数据。
- 首次启动引导标记。
不适合:
- 大列表。
- 下载记录。
- 播放历史。
- 复杂查询。
- 多表关系。
后面这些应该交给 RDB。
二. 用服务层封装 Preferences
页面直接调用系统 API 会让代码很难测,也很难替换缓存方案。建议先抽一个接口:
export interface KeyValueStorage {
getString(key: string, defaultValue: string): Promise<string>;
putString(key: string, value: string): Promise<void>;
getBoolean(key: string, defaultValue: boolean): Promise<boolean>;
putBoolean(key: string, value: boolean): Promise<void>;
}
页面或 Store 只依赖接口,不关心底层是 Preferences、内存 mock,还是后续迁移到安全存储。
三. 点赞缓存怎么设计
资讯列表里的点赞状态适合 Preferences,因为它是小规模键值状态。
class LikeStore {
private likedIds: Set<string> = new Set<string>();
async load() {
const raw = await this.storage.getString('liked_article_ids', '[]');
const ids: string[] = JSON.parse(raw) as string[];
this.likedIds = new Set<string>(ids);
}
async toggle(id: string) {
if (this.likedIds.has(id)) {
this.likedIds.delete(id);
} else {
this.likedIds.add(id);
}
await this.storage.putString('liked_article_ids', JSON.stringify(Array.from(this.likedIds)));
}
}
这里有两层状态:内存里的 Set 用于快速判断,Preferences 用于跨启动恢复。
四. UI 局部刷新怎么配合
点赞按钮的刷新范围可以尽量小。把点赞状态收敛到单个 item 或按钮组件,列表会更稳:
LikeBurstButton({
liked: item.liked,
onToggle: () => {
this.onToggleLike(item.id);
}
})
Store 更新某个 item 后,只让对应行重新渲染。这个案例很适合和 @Observed/@ObjectLink 或不可变 item 更新一起讲。
五. Preferences 也要处理异常
本地缓存读写看起来稳定,但仍然可能失败,比如 JSON 格式损坏、版本升级字段变化。
所以读取时要有兜底:
function parseStringArray(raw: string): string[] {
try {
const value = JSON.parse(raw) as string[];
return Array.isArray(value) ? value : [];
} catch (_) {
return [];
}
}
真实项目里还可以加版本号,方便以后迁移。
六. 三端对比
| 场景 | HarmonyOS | Flutter | iOS |
|---|
| 轻量键值 | Preferences | shared_preferences | UserDefaults |
| 大量结构化数据 | RDB | sqflite / drift | SQLite / CoreData |
| 安全敏感数据 | 安全存储策略 | flutter_secure_storage | Keychain |
所以选型思路很简单:小设置用键值,业务列表用数据库,敏感数据用安全存储。
七. 实践经验
第一,把完整列表 JSON 全塞进 Preferences,后期查询和迁移都痛苦。
第二,页面到处直接读写 key,key 名无法统一管理。
第三,只改内存状态,忘了持久化,重启后丢失。
第四,读取缓存没有异常兜底,一次坏数据导致页面打不开。
小结
Preferences 是鸿蒙本地缓存的第一站。它最适合轻量设置和少量状态,配合 Store 可以做出很自然的跨启动恢复。
下一篇我们讲 RDB,把播放历史、订阅、下载记录这些真正的业务数据放进结构化存储里。
今日练习
- 用 Preferences 缓存一个暗黑模式开关,重启后恢复。
- 用字符串数组保存点赞 id 集合,并处理 JSON 解析失败兜底。
- 说明为什么收听历史不适合直接塞进 Preferences。