鸿蒙应用开发:V1与V2版本数据持久化实战教程

作者:程序员黑豆日期:2026/8/6

这是一个使用鸿蒙技术开发的本地原生记账应用,非常适合大家用来练手。相关源码已上传至 Github,点击此处查看项目。欢迎大家交流、指正,也欢迎提交 PR。

一、引言

在鸿蒙应用开发中,数据持久化是构建完整应用体验的关键一环。无论是保存用户的登录状态、个人偏好,还是缓存应用的核心数据,都需要一套可靠且高效的持久化方案。随着HarmonyOS版本的演进,数据持久化能力也从V1版本进化到了V2版本,带来了更强大的功能和更灵活的使用方式。

本文将深入对比V1(PersistentStorage + AppStorage)和V2(PersistenceV2 + @ObservedV2 + @Trace)两种数据持久化方案,并通过一个“登录与个人中心”示例,带你掌握它们的核心用法、适用场景以及最佳实践。

二、V1版本:PersistentStorage + AppStorage

2.1 简介

V1版本的持久化方案基于 PersistentStorageAppStorage 的配合使用。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实例初始化完成后调用,否则持久化可能失败。
  • 类型限制:仅支持 numberstringbooleanenumMapSetDateundefinednull,不支持嵌套对象。
  • 性能考量:避免持久化大型数据集和频繁变化的变量,否则会影响性能。
  • 存储路径:存储路径为module级别,不同module使用相同key时,数据归属最先使用的module。

三、V2版本:PersistenceV2 + @ObservedV2 + @Trace

3.1 简介

V2版本引入了 PersistenceV2 单例对象,配合 @ObservedV2@Trace 装饰器,实现了更强大、更灵活的持久化能力。通过 connectglobalConnect 绑定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. 登录页面 - 保存登录状态

使用 @LocalPersistenceV2.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版本数据持久化实战教程》 是转载文章,点击查看原文


相关推荐


上线一个人静态网站需要多少钱,难不难?
五阳2026/7/28

不难,有合法身份证就能办,只需要花费 10 元,就能发布一个网站。 但是有门槛,先说门槛,如果你的网站需要中国大陆访问,那么必须要域名备案,不同省份等待时间不同。不过最多两周,最少 1 周也能审核完成。 有人问域名不备案就访问不通吗?不是的,我在阿里云申请的域名还没来得及备案,据我观察,大约有 2-3 周时间内,是允许中国大陆用户访问的,但是三周以后如果你还没有备案,那么就会被封掉,但是海外用户还能访问。 这是上线一个网站最困难和煎熬的环节了。 注意:国内个人用户备案域名,只能备案个人博客用途,


Agentic RAG 入门:从固定检索到自主决策
copyer_xyf2026/7/20

普通 RAG 的思路很直接:用户提出问题,系统从知识库检索相关片段,再让大模型根据这些片段生成回答。 这条链路解决了一个重要问题:模型不必只依赖训练时记住的知识,而是可以在回答前读取企业文档、产品手册、业务数据等外部资料,再根据资料组织答案。 但传统 RAG 的执行流程通常是固定的。这里的“固定”,不是说每次召回的文档都相同,而是说无论用户问什么、检索结果质量如何,程序都会按照上图展示的同一条路径执行。它不会在运行过程中重新判断,也不会主动改变检索策略。 但是,这种固定流程会遇到几个实际问题:


AEO 答案引擎优化:从“被搜索”到“被引用”的 AI 营销下一站
A亨日记2026/7/11

一、AI 正在改写“搜索”的默认行为 过去二十年,用户打开百度、Google 的第一件事是输入关键词,然后在十条蓝色链接里选择。但今天,越来越多的人直接向 DeepSeek、豆包、Kimi、ChatGPT 提问,并期待得到一个“直接可用”的答案。 这意味着企业的营销目标正在发生根本转移: SEO 时代:争取搜索结果页的前十排名;GEO 时代:争取被生成式引擎在回答中主动引用;AIO 时代:用结构化数据与反馈闭环持续优化 AI 对企业的“认知”;AEO 时代:直接为答案引擎生产“可被引用的高置信度


解决方案十八-企业级发音评测技术使用
Liu202605172026/7/3

在人工智能与教育深度融合的今天,语音评测技术(Speech Evaluation)已成为语言学习、在线教育、智能客服等领域的核心基础设施。无论是英语四六级口语考试、普通话水平测试,还是儿童语言启蒙应用,都离不开稳定、精准的语音评测能力。 然而,对于许多前端开发者而言,语音评测似乎总隔着一层“黑盒”——如何从前端采集音频?如何与云端AI服务建立稳定连接?如何处理流式数据并实时展示评测结果?这些问题往往让人望而却步。 本文将带你从零开始,基于Vue.js和WebSocket协议,构建一个完整的语


Claude Code上手指南:安装、部署、接入大模型
小此方2026/6/25

◆ 博主名称: 晓此方-CSDN博客 大家好,欢迎来到晓此方的博客。 ⭐️现代AI系列个人专栏: 【别传】AI应用与开发 ⭐️ “当AI能力被内化,智能就不再是成本,而是生产力。”——李彦宏 概要&序論    Hello,大家好,我是此方。 本文将带大家从零开始,完整走通 Claude Code 的安装、配置与部署流程。 一,让你的 Claude Code 动起来 1.1 安装前置软件 1


K-Means 聚类的目标函数:簇内误差平方和
F_D_Z2026/6/16

1. 什么是 K-Means? K-Means 是一种无监督、迭代式的聚类算法: 给定数据集 {x₁, x₂, …, xₙ} 与预设簇数 K,算法把样本划分为 K 个不相交的簇 C₁, C₂, …, Cₖ,使得同一簇内样本尽可能相似,不同簇间样本尽可能远离。 核心思想: > “让簇内‘抱团’,让簇间‘疏远’。” 2. 目标函数 J:簇内误差平方和(WCSS) K-Means 用几何距离衡量相似性,目标函数 J 定义为: J=∑k=1K∑x∈Ck∥x−μk∥2 J = \sum_{k=1}^{K


STM32F4 TIM定时器HAL库源码完全解析
冉卓电子2026/6/9

一、前言    在STM32嵌入式开发中,TIM定时器是使用频率最高、功能最丰富的外设之一,几乎所有嵌入式项目都会用到定时中断、PWM输出、输入捕获、编码器解码等功能,而这些功能的底层支撑都是TIM外设。   很多开发者只会调用HAL库现成函数,却不懂底层实现逻辑,遇到定时器卡死、PWM波形异常、捕获数据不准、DMA传输失效等问题时无从排查。   本文基于STM32F4xx HAL库 tim.c 完整源码,保姆级拆解TIM定时器全套底层架构、功能模块、核心函数、回调机制与使用规范,带你从源


【GaussDB】会话里出现大量idle in transaction状态的问题排查
DarkAthena2026/6/1

【GaussDB】会话里出现大量idle in transaction状态的问题排查 背景 客户DBA给应用开发人员宣讲GaussDB数据库的idle in transaction会话的监控告警后,开发人员自查,发现在开发环境中存在大量的idle in transaction会话,一时恐慌,于是来找DBA帮忙排查是不是应用代码哪里写得有问题。 数据库内核版本是 506.0 SPC0500 集中式。 检查应用 应用软件使用的spring框架接mybaitis,连接使用基于DBCP修改的连


Milvus 实战:当 RAG 遇上向量数据库,从"玩具 Demo"到"生产可用的"那一步
Lee川2026/5/25

Milvus 实战:当 RAG 遇上向量数据库,从"玩具 Demo"到"生产可用的"那一步 前面的文章讲过,RAG 的第四步是"向量存储"。在原型阶段,一个 MemoryVectorStore 就能跑通全链路。但问题是:程序一关,所有向量灰飞烟灭;数据一多,内存直接爆掉;更别提多用户隔离、权限控制、分布式检索这些生产环境的刚需。 MemoryVectorStore 是 Demo 的终点,却是向量数据库的起点。 本文通过一段与 Milvus(Zilliz Cloud)交互的真实代码,拆解向量数据库


RAG 系列(五):Embedding 模型——语义理解的核心
冬奇Lab2026/5/3

为什么换个 Embedding 模型,检索效果天差地别? 前面四篇文章,我们搞定了 Pipeline 搭建、参数调优和分块策略。但有一个问题一直没细说: 你的文档被切成 Chunk 之后,是怎么变成向量的? 这个过程叫 Embedding(嵌入),它把人类可读的文本变成计算机可算的向量。Embedding 模型的选择,直接决定了: "苹果"和"iPhone"能不能被识别为相关 "数据库连接池耗尽"和"Too many connections"能不能被匹配到一起 中文成语、专业术语、缩写能不

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读