鸿蒙应用开发:V1与V2版本数据持久化实战教程
文章目录
这是一个使用鸿蒙技术开发的本地原生记账应用,非常适合大家用来练手。相关源码已上传至 Github,点击此处查看项目。欢迎大家交流、指正,也欢迎提交 PR。
一、引言
在鸿蒙应用开发中,数据持久化是构建完整应用体验的关键一环。无论是保存用户的登录状态、个人偏好,还是缓存应用的核心数据,都需要一套可靠且高效的持久化方案。随着HarmonyOS版本的演进,数据持久化能力也从V1版本进化到了V2版本,带来了更强大的功能和更灵活的使用方式。
本文将深入对比V1(PersistentStorage + AppStorage)和V2(PersistenceV2 + @ObservedV2 + @Trace)两种数据持久化方案,并通过一个“登录与个人中心”示例,带你掌握它们的核心用法、适用场景以及最佳实践。
二、V1版本:PersistentStorage + AppStorage
2.1 简介
V1版本的持久化方案基于 PersistentStorage 和 AppStorage 的配合使用。PersistentStorage 负责将选定的 AppStorage 属性持久化到磁盘,并在应用重启时自动恢复。UI和业务逻辑不直接访问 PersistentStorage,所有属性访问都通过 AppStorage 进行,两者之间是双向同步的关系。
2.2 登录与个人中心示例
下面通过一个登录与个人中心的示例,展示V1版本的具体用法。
1. 初始化持久化数据
在 EntryAbility 中,初始化需要持久化的属性。
// 在UI实例初始化后调用
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err) => {
// PersistentStorage必须在UI实例初始化成功后(loadContent回调中)调用
// 早于此时机(onCreate/aboutToAppear)会导致持久化失效
PersistentStorage.persistProp('isLogin', false);
PersistentStorage.persistProp('userName', '');
PersistentStorage.persistProp('token', '');
});
}
2. 登录页面 - 保存登录状态
使用 @StorageLink 装饰器将UI变量与 AppStorage 中的属性绑定,修改UI变量会自动同步到 PersistentStorage。
import { promptAction, router } from '@kit.ArkUI';
@Entry
@Component
struct LoginPage {
@StorageLink('isLogin') isLogin: boolean = false;
@StorageLink('userName') userName: string = '';
@StorageLink('token') token: string = '';
build() {
Column({space: 20}) {
Text('当前登录状态:' + this.isLogin)
.fontSize(20)
Button('个人中心')
.width('80%')
.onClick(() => {
if (this.isLogin) {
router.push({
url: 'pages/ProfilePage'
})
} else {
promptAction.showToast({
message: '未登录,请先登录'
})
}
})
Button('登录')
.width('80%')
.onClick(() => {
// 模拟登录成功
this.isLogin = true;
this.userName = '张三';
this.token = 'abc123token';
// 数据自动同步到PersistentStorage持久化
router.push({
url: 'pages/ProfilePage'
})
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
运行效果:

3. 个人中心页面 - 读取持久化数据
同样使用 @StorageLink 读取持久化的数据,并在UI中展示。
import { router } from '@kit.ArkUI';
@Entry
@Component
struct ProfilePage {
@StorageLink('isLogin') isLogin: boolean = false;
@StorageLink('userName') userName: string = '';
@StorageLink('token') token: string = '';
build() {
Column({space: 20}) {
if (this.isLogin) {
Text(`用户名: ${this.userName}`)
.fontSize(20)
Text(`Token: ${this.token}`)
.fontSize(20)
} else {
Text('未登录')
.fontSize(20)
}
Button('返回')
.width('80%')
.onClick(() => {
router.back()
})
}
.width('100%')
.height('100%')
}
}
运行效果:

2.3 注意事项
- 调用时机:
PersistentStorage必须在UI实例初始化完成后调用,否则持久化可能失败。 - 类型限制:仅支持
number、string、boolean、enum、Map、Set、Date、undefined、null,不支持嵌套对象。 - 性能考量:避免持久化大型数据集和频繁变化的变量,否则会影响性能。
- 存储路径:存储路径为module级别,不同module使用相同key时,数据归属最先使用的module。
三、V2版本:PersistenceV2 + @ObservedV2 + @Trace
3.1 简介
V2版本引入了 PersistenceV2 单例对象,配合 @ObservedV2 和 @Trace 装饰器,实现了更强大、更灵活的持久化能力。通过 connect 或 globalConnect 绑定key,修改被 @Trace 装饰的属性时会自动触发持久化和UI更新。globalConnect 从API 18开始支持,存储路径为应用级别,是推荐的使用方式。
3.2 登录与个人中心示例
1. 定义持久化数据模型
使用 @ObservedV2 装饰类,并在需要持久化的属性上使用 @Trace 装饰器。
// src/main/ets/model/UserInfo.ets
@ObservedV2
export class UserInfo {
@Trace isLogin: boolean = false;
@Trace userName: string = '';
@Trace token: string = '';
// 普通属性不会触发自动持久化
lastLoginTime: string = '';
}
2. 登录页面 - 保存登录状态
使用 @Local 和 PersistenceV2.connect 获取持久化数据实例,直接修改 @Trace 属性即可。
import { PersistenceV2 } from '@kit.ArkUI';
import { UserInfo } from '../model/UserInfo';
import router from '@ohos.router';
import promptAction from '@ohos.promptAction';
@Entry
@ComponentV2
struct LoginPage {
@Local userInfo: UserInfo =
PersistenceV2.connect(UserInfo, 'userInfoKey', () => new UserInfo())!;
build() {
Column({space: 20}) {
Text('当前登录状态:' + this.userInfo.isLogin)
.fontSize(20)
Button('个人中心')
.width('80%')
.onClick(() => {
if (this.userInfo.isLogin) {
router.push({
url: 'pages/ProfilePage'
})
} else {
promptAction.showToast({ message: '未登录,请先登录' })
}
})
Button('登录')
.width('80%')
.onClick(() => {
// 直接修改@Trace属性,自动触发持久化和UI更新
this.userInfo.isLogin = true;
this.userInfo.userName = '张三';
this.userInfo.token = 'abc123token';
// 注意:lastLoginTime是普通属性,不会自动持久化
router.push({
url: 'pages/ProfilePage'
})
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
运行效果:

3. 个人中心页面 - 读取持久化数据
同样使用 PersistenceV2.connect 获取数据实例,UI会自动响应 @Trace 属性的变化。
import { PersistenceV2 } from '@kit.ArkUI';
import { UserInfo } from '../model/UserInfo';
import router from '@ohos.router';
@Entry
@ComponentV2
struct ProfilePage {
@Local userInfo: UserInfo =
PersistenceV2.connect(UserInfo, 'userInfoKey', () => new UserInfo())!;
build() {
Column({space: 20}) {
if (this.userInfo.isLogin) {
Text(`用户名: ${this.userInfo.userName}`)
.fontSize(20)
Text(`Token: ${this.userInfo.token}`)
.fontSize(20)
} else {
Text('未登录')
.fontSize(20)
}
Button('返回')
.width('80%')
.onClick(() => {
router.back()
})
}
.width('100%')
.height('100%')
}
}
运行效果:

4. 整体赋值场景
当需要从服务器获取完整用户信息并整体赋值时,需要注意V2的机制。
// 方式一:逐个属性赋值(推荐)
const serverData = { isLogin: true, userName: '李四', token: 'newToken' };
this.userInfo.isLogin = serverData.isLogin;
this.userInfo.userName = serverData.userName;
this.userInfo.token = serverData.token;
// 方式二:先删除再重新连接(适用于大量属性)
PersistenceV2.remove('userInfoKey');
const newUserInfo = new UserInfo();
newUserInfo.isLogin = true;
newUserInfo.userName = '李四';
newUserInfo.token = 'newToken';
// 重新connect获取新实例
3.3 注意事项
- @Trace装饰器:只有被
@Trace装饰的属性变更才会触发自动持久化,普通属性、V1状态变量、@Observed对象的变化不会触发。 - 整体赋值:直接用新对象覆盖会导致持久化失效,需要逐个属性赋值或先删除再重新
connect。 - 数据量控制:不宜大量持久化数据,可能导致页面卡顿。
- 调用时机:持久化操作需在UI实例初始化完成后调用(
loadContent回调触发后)。 - API版本:
PersistenceV2从API 12开始支持,globalConnect从API 18开始支持,建议使用globalConnect(应用级存储路径)。
四、V1与V2核心区别总结
| 对比项 | V1 (PersistentStorage) | V2 (PersistenceV2) |
|---|---|---|
| 数据模型 | 仅支持基本类型,不支持嵌套对象 | 支持复杂对象(需 @ObservedV2 + @Trace) |
| 触发持久化 | 通过 AppStorage 属性变更自动同步 |
仅 @Trace 属性变更触发自动持久化 |
| 存储路径 | module级别 | connect 为module级,globalConnect 为应用级 |
| 整体赋值 | 直接赋值即可 | 需逐个属性赋值或先删后建 |
| 适用场景 | 简单键值对存储 | 复杂UI状态持久化 |
| 性能 | 适合小数据量 | 不宜大量数据,避免卡顿 |
五、总结与选择建议
- V1方案:简单、直接,适合存储登录状态、用户偏好等简单的键值对数据。如果你的数据结构不复杂,且不需要跨module共享,V1是一个轻量级的选择。
- V2方案:功能强大,支持复杂对象和更精细的持久化控制。当你需要持久化复杂的UI状态、多页面共享状态,或者希望获得更灵活的存储路径(应用级)时,推荐使用V2的
globalConnect。
选择哪种方案取决于你的具体需求。对于大多数现代鸿蒙应用,尤其是涉及复杂数据交互的场景,V2方案是更优的选择。
更多推荐




所有评论(0)