这是一个使用鸿蒙技术开发的本地原生记账应用,非常适合大家用来练手。相关源码已上传至 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 中,初始化需要持久化的属性。
1// 在UI实例初始化后调用 2onWindowStageCreate(windowStage: window.WindowStage): void { 3 windowStage.loadContent('pages/Index', (err) => { 4 // PersistentStorage必须在UI实例初始化成功后(loadContent回调中)调用 5 // 早于此时机(onCreate/aboutToAppear)会导致持久化失效 6 PersistentStorage.persistProp('isLogin', false); 7 PersistentStorage.persistProp('userName', ''); 8 PersistentStorage.persistProp('token', ''); 9 }); 10 } 11
2. 登录页面 - 保存登录状态
使用 @StorageLink 装饰器将UI变量与 AppStorage 中的属性绑定,修改UI变量会自动同步到 PersistentStorage。
1import { promptAction, router } from '@kit.ArkUI'; 2 3@Entry 4@Component 5struct LoginPage { 6 @StorageLink('isLogin') isLogin: boolean = false; 7 @StorageLink('userName') userName: string = ''; 8 @StorageLink('token') token: string = ''; 9 10 build() { 11 Column({space: 20}) { 12 Text('当前登录状态:' + this.isLogin) 13 .fontSize(20) 14 Button('个人中心') 15 .width('80%') 16 .onClick(() => { 17 if (this.isLogin) { 18 router.push({ 19 url: 'pages/ProfilePage' 20 }) 21 } else { 22 promptAction.showToast({ 23 message: '未登录,请先登录' 24 }) 25 } 26 }) 27 28 Button('登录') 29 .width('80%') 30 .onClick(() => { 31 // 模拟登录成功 32 this.isLogin = true; 33 this.userName = '张三'; 34 this.token = 'abc123token'; 35 // 数据自动同步到PersistentStorage持久化 36 37 router.push({ 38 url: 'pages/ProfilePage' 39 }) 40 }) 41 } 42 .width('100%') 43 .height('100%') 44 .justifyContent(FlexAlign.Center) 45 } 46} 47
运行效果:
3. 个人中心页面 - 读取持久化数据
同样使用 @StorageLink 读取持久化的数据,并在UI中展示。
1import { router } from '@kit.ArkUI'; 2 3@Entry 4@Component 5struct ProfilePage { 6 @StorageLink('isLogin') isLogin: boolean = false; 7 @StorageLink('userName') userName: string = ''; 8 @StorageLink('token') token: string = ''; 9 10 build() { 11 Column({space: 20}) { 12 if (this.isLogin) { 13 Text(`用户名: ${this.userName}`) 14 .fontSize(20) 15 Text(`Token: ${this.token}`) 16 .fontSize(20) 17 } else { 18 Text('未登录') 19 .fontSize(20) 20 } 21 22 Button('返回') 23 .width('80%') 24 .onClick(() => { 25 router.back() 26 }) 27 } 28 .width('100%') 29 .height('100%') 30 } 31} 32
运行效果:
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 装饰器。
1// src/main/ets/model/UserInfo.ets 2@ObservedV2 3export class UserInfo { 4 @Trace isLogin: boolean = false; 5 @Trace userName: string = ''; 6 @Trace token: string = ''; 7 // 普通属性不会触发自动持久化 8 lastLoginTime: string = ''; 9} 10
2. 登录页面 - 保存登录状态
使用 @Local 和 PersistenceV2.connect 获取持久化数据实例,直接修改 @Trace 属性即可。
1import { PersistenceV2 } from '@kit.ArkUI'; 2import { UserInfo } from '../model/UserInfo'; 3import router from '@ohos.router'; 4import promptAction from '@ohos.promptAction'; 5 6@Entry 7@ComponentV2 8struct LoginPage { 9 @Local userInfo: UserInfo = 10 PersistenceV2.connect(UserInfo, 'userInfoKey', () => new UserInfo())!; 11 12 build() { 13 Column({space: 20}) { 14 Text('当前登录状态:' + this.userInfo.isLogin) 15 .fontSize(20) 16 17 Button('个人中心') 18 .width('80%') 19 .onClick(() => { 20 if (this.userInfo.isLogin) { 21 router.push({ 22 url: 'pages/ProfilePage' 23 }) 24 } else { 25 promptAction.showToast({ message: '未登录,请先登录' }) 26 } 27 }) 28 Button('登录') 29 .width('80%') 30 .onClick(() => { 31 // 直接修改@Trace属性,自动触发持久化和UI更新 32 this.userInfo.isLogin = true; 33 this.userInfo.userName = '张三'; 34 this.userInfo.token = 'abc123token'; 35 // 注意:lastLoginTime是普通属性,不会自动持久化 36 37 router.push({ 38 url: 'pages/ProfilePage' 39 }) 40 }) 41 } 42 .width('100%') 43 .height('100%') 44 .justifyContent(FlexAlign.Center) 45 } 46} 47
运行效果:
3. 个人中心页面 - 读取持久化数据
同样使用 PersistenceV2.connect 获取数据实例,UI会自动响应 @Trace 属性的变化。
1import { PersistenceV2 } from '@kit.ArkUI'; 2import { UserInfo } from '../model/UserInfo'; 3import router from '@ohos.router'; 4 5@Entry 6@ComponentV2 7struct ProfilePage { 8 @Local userInfo: UserInfo = 9 PersistenceV2.connect(UserInfo, 'userInfoKey', () => new UserInfo())!; 10 11 build() { 12 Column({space: 20}) { 13 if (this.userInfo.isLogin) { 14 Text(`用户名: ${this.userInfo.userName}`) 15 .fontSize(20) 16 Text(`Token: ${this.userInfo.token}`) 17 .fontSize(20) 18 } else { 19 Text('未登录') 20 .fontSize(20) 21 } 22 23 Button('返回') 24 .width('80%') 25 .onClick(() => { 26 router.back() 27 }) 28 } 29 .width('100%') 30 .height('100%') 31 } 32} 33
运行效果:
4. 整体赋值场景
当需要从服务器获取完整用户信息并整体赋值时,需要注意V2的机制。
1// 方式一:逐个属性赋值(推荐) 2const serverData = { isLogin: true, userName: '李四', token: 'newToken' }; 3this.userInfo.isLogin = serverData.isLogin; 4this.userInfo.userName = serverData.userName; 5this.userInfo.token = serverData.token; 6 7// 方式二:先删除再重新连接(适用于大量属性) 8PersistenceV2.remove('userInfoKey'); 9const newUserInfo = new UserInfo(); 10newUserInfo.isLogin = true; 11newUserInfo.userName = '李四'; 12newUserInfo.token = 'newToken'; 13// 重新connect获取新实例 14
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方案是更优的选择。
《鸿蒙应用开发:V1与V2版本数据持久化实战教程》 是转载文章,点击查看原文。