《静默登录》二、持久化存储UI状态使用指南
PersistentStorage:持久化存储 UI 状态使用指南
效果
一、概述
PersistentStorage 是 HarmonyOS 应用中的可选单例对象,用于将选定的 AppStorage 属性持久化存储到设备磁盘。其核心价值在于:应用重启后,被持久化的状态变量能够自动恢复到上一次关闭时的值。
1.1 核心特性
- 自动持久化:数据变更时自动同步写入磁盘,无需手动调用保存方法
- 与 AppStorage 深度集成:通过
@StorageLink和@StorageProp实现双向/单向数据绑定 - 轻量便捷:适合存储小型 UI 状态数据(建议小于 2KB)
1.2 适用场景
| 场景 | 说明 |
|---|---|
| 登录状态记忆 | 记住用户是否已登录、登录昵称、手机号、是否保持登录 |
| 主题切换 | 保存深色/浅色模式偏好 |
| 页面标签索引 | 记住用户当前选中的 Tab 页 |
| 简单配置项 | 字体大小、语言设置等 |
| 搜索历史关键词 | 保存最近的搜索词 |
1.3 不适用场景
- 大量结构化数据(建议使用关系型数据库 RDB)
- 高频变化的数据(如定时器计数、动画帧数据)
- 复杂对象或大数组(超过 2KB 会影响 UI 性能)
二、核心概念与工作原理
2.1 数据流向
UI 组件 ←→ AppStorage ←→ PersistentStorage ←→ 磁盘文件
PersistentStorage将指定 key 注册为持久化属性- 该属性同步到
AppStorage中 - UI 组件通过
@StorageLink/@StorageProp绑定该属性 - 属性值变化时,自动写入磁盘;应用重启时,自动从磁盘读回
2.2 关键 API
| 方法 | 说明 |
|---|---|
persistProp(key, defaultValue) |
持久化单个属性,若已有值则保留,否则使用默认值 |
persistProps(obj) |
批量持久化多个属性 |
deleteProp(key) |
删除持久化属性(不删除 AppStorage 中的值) |
clear() |
清除所有持久化属性 |
2.3 初始化时机
PersistentStorage 必须在 UI 实例初始化成功后才能调用,即在 loadContent 的回调中调用:
// EntryAbility.ets
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err) => {
if (err.code) return;
// 正确时机:loadContent 回调中
PersistentStorage.persistProp('isDarkMode', false);
});
}
注意:如果在
loadContent之前调用,会导致持久化失败。
三、基础用法
3.1 持久化单个属性(persistProp)
步骤一:在 EntryAbility 中初始化持久化属性
// EntryAbility.ets
windowStage.loadContent('pages/GlowHomePage', (err) => {
if (err.code) return;
// 静默登录开关(仅记录用户意愿)
PersistentStorage.persistProp('silentLoginEnabled', false);
PersistentStorage.persistProp('lastPhone', '');
// 登录状态(跨页面实时同步)
PersistentStorage.persistProp('isLoggedIn', false);
PersistentStorage.persistProp('loggedInNickname', '');
});
步骤二:登录页写入登录状态(@StorageLink 双向绑定)
// GlowLoginPage.ets
@StorageLink('isLoggedIn') isLoggedIn: boolean = false;
@StorageLink('loggedInNickname') loggedInNickname: string = '';
@StorageLink('lastPhone') lastPhone: string = '';
async handleLogin(): Promise<void> {
// ... 查询/注册成功后
this.isLoggedIn = true; // 主页立即响应“已登录”
this.loggedInNickname = existingUser.nickname; // 主页立即显示昵称
this.lastPhone = this.phoneInput.trim();
}
步骤三:首页读取登录状态并支持退出
// GlowHomePage.ets
@StorageLink('isLoggedIn') isLoggedIn: boolean = false;
@StorageLink('loggedInNickname') loggedInNickname: string = '';
@StorageLink('lastPhone') lastPhone: string = '';
logout(): void {
this.isLoggedIn = false;
this.loggedInNickname = '';
this.lastPhone = '';
}
// UI 中使用
Text(this.isLoggedIn ? `Hi, ${this.loggedInNickname}` : '探索无限可能')
关键点:
isLoggedIn和loggedInNickname使用@StorageLink双向绑定,登录页写入后主页无需刷新即可实时响应。
@Component
struct SettingsPage {
@StorageLink('isRemembered') isRemembered: boolean = false;
@StorageLink('themeColor') themeColor: string = 'blue';
build() {
Column() {
Toggle({ type: ToggleType.Switch, isOn: this.isRemembered })
.onChange((isOn: boolean) => {
this.isRemembered = isOn; // 自动持久化
})
Text(`当前主题:${this.themeColor}`)
.onClick(() => {
this.themeColor = this.themeColor === 'blue' ? 'green' : 'blue';
})
}
}
}
@StorageLink实现双向绑定:组件内修改值会自动同步到AppStorage,并触发PersistentStorage持久化写入。
3.2 单向读取(@StorageProp)
如果只需要读取值,不需要反向同步,使用 @StorageProp:
@Component
struct DisplayPage {
@StorageProp('isRemembered') isRemembered: boolean = false;
build() {
Text(this.isRemembered ? '已记住登录状态' : '未记住登录状态')
}
}
@StorageProp是单向绑定:AppStorage变化会同步到组件,但组件内修改不会回写。
3.3 批量持久化(persistProps)
一次性持久化多个属性:
PersistentStorage.persistProps({
'fontSize': 16,
'language': 'zh-CN',
'isNotificationOn': true
});
3.4 访问与修改
通过 AppStorage 的 API 可以直接访问和修改持久化属性:
// 读取
let value = AppStorage.get<boolean>('isRemembered');
// 修改(自动触发持久化)
AppStorage.set<boolean>('isRemembered', true);
// 不存在则创建
AppStorage.setOrCreate('newKey', 'hello');
四、完整示例:搜索历史关键词持久化
以下示例展示一个搜索页面,使用 PersistentStorage 保存用户的搜索历史关键词(最近 5 条)。
4.1 EntryAbility 初始化
// EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {}
async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
windowStage.loadContent('pages/SearchPage', (err) => {
if (err.code) return;
// 持久化搜索历史,默认为空数组
PersistentStorage.persistProp('searchHistory', [] as string[]);
});
}
onDestroy(): void {}
onForeground(): void {}
onBackground(): void {}
}
4.2 搜索页面组件
// SearchPage.ets
@Entry
@Component
struct SearchPage {
@State searchText: string = '';
@StorageLink('searchHistory') searchHistory: string[] = [];
addSearchHistory(keyword: string): void {
// 去除重复
const index = this.searchHistory.indexOf(keyword);
if (index > -1) {
this.searchHistory.splice(index, 1);
}
// 插入到最前面
this.searchHistory.unshift(keyword);
// 只保留最近 5 条
if (this.searchHistory.length > 5) {
this.searchHistory.pop();
}
}
clearHistory(): void {
this.searchHistory = [];
}
build() {
Column() {
// 搜索栏
Row() {
TextInput({ placeholder: '搜索...' })
.onChange((value: string) => { this.searchText = value; })
.layoutWeight(1)
.height(40)
Button('搜索')
.onClick(() => {
if (this.searchText.trim() !== '') {
this.addSearchHistory(this.searchText.trim());
this.searchText = '';
}
})
.margin({ left: 10 })
}
.width('90%')
.padding({ top: 20 })
// 搜索历史
if (this.searchHistory.length > 0) {
Row() {
Text('搜索历史')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Text('清空')
.fontSize(14)
.fontColor('#999999')
.onClick(() => { this.clearHistory(); })
}
.width('90%')
.margin({ top: 20 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.searchHistory, (item: string) => {
Text(item)
.fontSize(14)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.backgroundColor('#f0f0f0')
.borderRadius(16)
.margin({ right: 8, top: 8 })
.onClick(() => {
this.searchText = item;
})
})
}
.width('90%')
}
}
.width('100%')
.height('100%')
}
}
4.3 运行效果说明
- 首次运行:搜索历史为空,输入关键词点击搜索后,关键词被添加到历史列表
- 退出应用:搜索历史自动持久化到磁盘
- 重新启动:搜索历史自动恢复,显示之前保存的关键词
- 最多保留 5 条:新搜索词插入到最前面,超过 5 条时自动移除最旧的
五、进阶示例:深色模式切换
// EntryAbility.ets
windowStage.loadContent('pages/ThemePage', (err) => {
if (err.code) return;
PersistentStorage.persistProp('isDarkMode', false);
});
// ThemePage.ets
@Entry
@Component
struct ThemePage {
@StorageLink('isDarkMode') isDarkMode: boolean = false;
build() {
Column() {
Row() {
Text('深色模式')
.fontSize(18)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.isDarkMode })
.selectedColor('#007DFF')
.onChange((isOn: boolean) => {
this.isDarkMode = isOn;
})
}
.width('80%')
.padding(20)
Column() {
Text('这是一段示例文本')
.fontSize(16)
.fontColor(this.isDarkMode ? '#ffffff' : '#000000')
Text('当前为' + (this.isDarkMode ? '深色' : '浅色') + '模式')
.fontSize(14)
.fontColor(this.isDarkMode ? '#aaaaaa' : '#666666')
.margin({ top: 10 })
}
.width('80%')
.padding(20)
.backgroundColor(this.isDarkMode ? '#333333' : '#f5f5f5')
.borderRadius(12)
.margin({ top: 20 })
}
.width('100%')
.height('100%')
.backgroundColor(this.isDarkMode ? '#1a1a1a' : '#ffffff')
}
}
六、注意事项与最佳实践
6.1 性能注意
| 要点 | 说明 |
|---|---|
| 数据大小 | 单个属性建议小于 2KB,大量数据请使用 RDB |
| 写入时机 | 写入磁盘在 UI 线程同步执行,大对象会阻塞渲染 |
| 高频变更 | 避免持久化频繁变化的数据(如动画帧、定时器) |
6.2 初始化顺序
1. loadContent 加载页面
2. 在 loadContent 回调中调用 PersistentStorage.persistProp()
3. 组件中使用 @StorageLink / @StorageProp 绑定
重要:如果先在
AppStorage中创建了同名属性,再调用PersistentStorage.persistProp,会使用AppStorage中已有的值,而非磁盘中的值。建议先调用PersistentStorage。
6.3 数据类型支持
PersistentStorage 支持以下数据类型:
- 基础类型:
string、number、boolean - 对象和数组(需可 JSON 序列化)
- 不支持:
Map、Set、Date、RegExp等复杂对象
6.4 常见误区
| 误区 | 正确做法 |
|---|---|
在 onCreate 中调用 persistProp |
应在 loadContent 回调中调用 |
| 存储大对象或长数组 | 使用关系型数据库 RDB |
在组件 aboutToAppear 中初始化持久化 |
初始化应在 Ability 的 loadContent 回调中 |
@StorageProp 尝试修改值 |
需修改时使用 @StorageLink |
登录状态仅存本地 @State,跨页面不同步 |
使用 @StorageLink 存入 AppStorage,跨页面实时响应 |
只持久化 lastPhone,不持久化 isLoggedIn |
同时持久化 isLoggedIn + loggedInNickname,避免重启后状态不一致 |
七、API 速查表
PersistentStorage 静态方法
| 方法 | 参数 | 说明 |
|---|---|---|
persistProp(key, defaultValue) |
key: string, defaultValue: T | 持久化单个属性 |
persistProps(obj) |
obj: Object | 批量持久化多个属性 |
deleteProp(key) |
key: string | 删除持久化属性 |
clear() |
无 | 清除所有持久化属性 |
组件内绑定装饰器
| 装饰器 | 方向 | 说明 |
|---|---|---|
@StorageLink(key) |
双向 | 组件修改会同步到 AppStorage 并持久化 |
@StorageProp(key) |
单向 | 只能读取 AppStorage 中的值 |
八、总结
PersistentStorage 是 HarmonyOS 中最轻量的状态持久化方案,核心优势是零代码自动持久化。只需三步即可使用:
- 初始化:在
loadContent回调中调用persistProp - 绑定:组件中使用
@StorageLink双向绑定 - 使用:正常读写状态变量,变更自动持久化
适合存储登录状态、主题偏好、Tab 索引等小型 UI 状态数据,是 HarmonyOS 状态管理体系中不可或缺的一环。
更多推荐



所有评论(0)