文章目录
-
- 一、简介
- 二、核心概念与用法详解
-
- [2.1 核心 API 详解](#2.1 核心 API 详解)
- [三、实战示例:登录 → 个人中心](#三、实战示例:登录 → 个人中心)
-
- [3.1 定义共享数据模型](#3.1 定义共享数据模型)
- [3.2 登录页面(LoginPage)](#3.2 登录页面(LoginPage))
- [3.3 个人中心页面(ProfilePage)](#3.3 个人中心页面(ProfilePage))
- [3.4 主页面与页面跳转](#3.4 主页面与页面跳转)
- [3.5 运行效果验证](#3.5 运行效果验证)
- 四、最佳实践与数据清理
- 五、总结与对比
一、简介
AppStorageV2 是 HarmonyOS 提供的应用级全局状态管理方案。它通过键值对方式在应用的所有组件(跨 Ability、跨页面、跨组件层级)之间共享数据,且支持响应式更新------当共享数据发生变化时,所有绑定了该数据的 UI 会自动刷新。
AppStorageV2 并非 AppStorage 的简单升级,而是为状态管理 V2 架构设计的全新全局状态存储方案。
它是为了突破 V1 在响应式能力、数据流方向、与新型组件兼容性上的瓶颈而推出的。旧项目可继续使用 AppStorage,但新项目或需要向 V2 架构迁移的项目,官方建议优先使用 AppStorageV2,以获得更现代、更可靠的全局状态管理体验。
二、核心概念与用法详解
使用 AppStorageV2 主要分为三步:
- 定义数据类 :创建一个普通类,使用
@ObservedV2装饰器标记它,并用@Trace装饰器标记你希望响应式更新的属性。 - 连接全局存储 :在任何一个组件中,使用
AppStorageV2.connect(ClassName, factory)来获取或创建一个与该类绑定的全局单例。 - 读写与响应 :直接通过
connect返回的实例读写被@Trace修饰的属性。所有connect了同一个类的组件都会自动收到变化通知并刷新 UI。
注意:如果你希望数据变化后 UI 自动刷新,并且希望获得属性级精准更新、避免无效渲染,那就必须用
@ObservedV2+ @Trace修饰类及其属性。如果你只把AppStorageV2当作一个纯数据容器,且不需要 UI 自动响应内部属性的变化,完全可以去掉装饰器。
2.1 核心 API 详解
@ObservedV2 类装饰器
它告诉 AppStorageV2 系统,这个类是一个可被观察的数据模型。只有被它装饰的类,其实例才能被 AppStorageV2 管理。
@Trace 属性装饰器
标记在类属性上,表示"当这个属性的值发生变化时,需要通知所有观察者"。这是实现响应式的关键。没有被 @Trace 修饰的属性,即使所属类有 @ObservedV2,其变化也不会触发 UI 刷新。
AppStorageV2.connect<T>(type: new () => T, factory: () => T): T
此 API 用于连接到或创建一个指定类型的全局实例。
type: 你定义的、被@ObservedV2装饰的类的构造函数。factory: 一个工厂函数。如果该类型的实例尚不存在于全局存储中,则会调用此函数创建一个新的;如果已存在,则直接返回已存在的实例。
注意:在整个应用进程中,同一个 Class 只会存在一个全局实例(单例模式)。
三、实战示例:登录 → 个人中心
下面通过一个完整的例子,演示如何在登录页写入用户信息,然后在个人中心页读取并展示。你将看到,两页之间无需通过路由传参,数据就能自动同步。
3.1 定义共享数据模型
首先,创建一个独立的文件 src/main/ets/model/UserData.ets 来存放我们的数据模型。
typescript
@ObservedV2
export class UserData {
@Trace username: string = '';
@Trace isLoggedIn: boolean = false;
}
3.2 登录页面(LoginPage)
typescript
// src/main/ets/pages/LoginPage.ets
import { AppStorageV2, router } from '@kit.ArkUI';
import { UserData } from '../model/UserData';
@Entry
@ComponentV2
export struct LoginPage {
@Local userData: UserData = AppStorageV2.connect(UserData, () => new UserData())!;
@Local inputValue: string = '';
build() {
Column({ space: 16 }) {
Text('登录页')
.fontSize(24)
.fontWeight(FontWeight.Bold)
TextInput({ placeholder: '请输入用户名', text: this.inputValue })
.onChange((value: string) => {
this.inputValue = value;
})
.width('80%')
Button('登录')
.width('80%')
.onClick(() => {
if (this.inputValue.trim().length > 0) {
this.userData.username = this.inputValue.trim();
this.userData.isLoggedIn = true;
router.push({
url: 'pages/ProfilePage'
})
}
})
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
关键点 :@Local userData 通过 AppStorageV2.connect 初始化。当我们修改 this.userData.username 时,这个更改会立刻同步到全局存储中。
运行效果:

3.3 个人中心页面(ProfilePage)
typescript
// src/main/ets/pages/ProfilePage.ets
import { AppStorageV2 } from '@kit.ArkUI';
import { UserData } from '../model/UserData';
@Entry
@ComponentV2
export struct ProfilePage {
@Local userData: UserData = AppStorageV2.connect(UserData, () => new UserData())!;
build() {
Column({ space: 16 }) {
Text('个人中心')
.fontSize(24)
.fontWeight(FontWeight.Bold)
if (this.userData.isLoggedIn) {
Text(`欢迎您,${this.userData.username}`)
.fontSize(20)
.fontColor('#FF6600')
} else {
Text('未登录')
.fontSize(18)
.fontColor(Color.Gray)
}
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
关键点 :ProfilePage 也通过同样的代码 AppStorageV2.connect(UserData, () => new UserData())! 获取实例。因为 UserData 类型的全局实例已经在 LoginPage 中创建,所以这里只会获取并复用同一个实例,从而读取到最新的用户数据。
运行效果:

3.4 主页面与页面跳转
typescript
// src/main/ets/pages/Index.ets
import { router } from '@kit.ArkUI';
import { AppStorageV2 } from '@kit.ArkUI';
import { UserData } from '../model/UserData';
@Entry
@ComponentV2
struct Index {
@Local userData: UserData = AppStorageV2.connect(UserData, () => new UserData())!;
build() {
Column({ space: 20 }) {
Button('去登录')
.width('80%')
.onClick(() => {
router.pushUrl({
url: 'pages/LoginPage'
});
})
Button('去个人中心')
.width('80%')
.onClick(() => {
router.pushUrl({
url: 'pages/ProfilePage'
});
})
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
3.5 运行效果验证
- 启动应用,点击"去登录"。
- 在登录页输入用户名,点击"登录"。
- 返回主页,点击"去个人中心",你会看到用户名已经自动显示为刚才输入的值。
- 再次回到登录页,修改用户名并再次登录,然后重新进入个人中心,你会发现用户名也同步更新了。整个过程无需任何组件间手动传参。
运行效果:

四、最佳实践与数据清理
AppStorageV2 的全局实例与应用进程共存。如果你在登录时存储了用户信息,但在用户退出登录时不清除,那么下次打开应用时,应用可能还会显示上次登录的用户名,这不是我们想要的行为。
因此,强烈建议 在用户退出登录时,使用 AppStorageV2.remove() 方法手动清理数据。
typescript
// 在退出登录的方法中调用
import { AppStorageV2 } from '@kit.arkui';
import { UserData } from '../model/UserData';
function logout() {
// ... 执行其他退出逻辑
// 移除全局存储中的 UserData 实例
AppStorageV2.remove(UserData);
}
调用 remove 后,所有 connect 到 UserData 的组件都会被通知,并尝试重新通过 factory 函数创建新的实例,从而恢复到初始状态。
运行效果:

五、总结与对比
| 要点 | 说明 |
|---|---|
需要响应式的属性用 @Trace |
只标记你需要响应 UI 变化的属性,避免不必要的性能开销。 |
connect 返回的是同一个实例 |
理解这个"单例"特性是正确使用的关键。 |
退出登录时建议 remove |
这是数据清理的好习惯,避免旧数据污染新会话。 |
| 生命周期为应用进程级 | 与 @State、@Prop 等组件级状态管理不同,它是全局的。 |
AppStorageV2 非常适合管理全局用户登录状态、应用主题配置、多语言等需要在不同页面间共享的数据。相比于 AppStorageV1(使用键值字符串存取),V2 版本提供了更现代、类型安全、面向对象的使用方式,是 HarmonyOS 全局状态管理的首选方案。