写在前面
如果你写过鸿蒙 ArkUI 应用,大概率遇到过这个场景:
你写了个主题切换功能,V1 用
AppStorage.set('theme', 'dark')存应用级状态------结果改了之后 UI 不更新,得手动this.theme = AppStorage.get('theme')同步。 你查文档发现 V1 的@StorageLink/@StorageProp能自动同步------但只能用在@Component里,V2 组件用不了。 你查文档发现鸿蒙 6.1 给了 V2 的AppStorageV2------但点进去发现它没有set/get,只有connect拿可观察对象。API 一脸懵。
这是 V1「set/get 手动同步」和 V2「connect 拿可观察对象」的分水岭。鸿蒙 6.1 给的答案是 AppStorageV2 ------connect(class, key, default) 拿应用级可观察对象,改成员就真触发 UI 重绘,不用手动同步。
本文就用一个真机可跑 的「应用级主题/语言切换」demo,把 AppStorageV2 从「听名字一脸懵」讲到「下个项目直接抄」。代码托管在 AtomGit,文末有链接,真机实拍截图作证。这是 V2 装饰器系列第二篇,接续上篇 @ObservedV2 + @Trace。
适合人群:写过鸿蒙应用、被 V1
AppStorage手动同步折磨过的同学。 不适合人群:还在学@State的同学------出门左转看我的入门篇。
一、先讲清楚:AppStorageV2 到底是啥
一句话:AppStorageV2 是鸿蒙 6.1 V2 应用级状态,用 connect 拿可观察对象,改成员就真重绘。
V1 的 AppStorage.set/get 是键值对存取------改了不自动同步 UI,要手动 @StorageLink 或 this.x = AppStorage.get()。V2 的 AppStorageV2.connect 直接拿可观察对象(@ObservedV2 + @Trace 装的 class),改成员就真触发 UI 重绘,不用手动同步。
核心 API 一览:
| API | 作用 | 一句话理解 |
|---|---|---|
AppStorageV2.connect(type, key, default) |
拿应用级可观察对象 | 「有就拿,没有就用 default 新建」 |
AppStorageV2.remove(key) |
删应用级键 | 「清理应用级状态」 |
AppStorageV2.keys() |
看所有应用级键 | 「列出当前存了哪些」 |
注意:V2 没有 set/get------这是跟 V1 最大的区别,新手最容易踩的坑。
二、动手:一个应用级主题/语言切换的 demo
2.1 定义 @ObservedV2 class + import
typescript
import { AppStorageV2 } from '@ohos.arkui.StateManagement'
@ObservedV2
class AppConfig {
@Trace theme: string = 'light'
@Trace lang: string = 'zh'
}
三个细节:
import { AppStorageV2 } from '@ohos.arkui.StateManagement'------V2 应用级状态需显式 import,不是全局名@ObservedV2 class AppConfig------应用级状态必须用@ObservedV2class,成员装@Trace@Trace theme/@Trace lang------追踪这两个成员变化,改了就触发 UI 重绘
2.2 connect 拿应用级可观察对象
typescript
@Entry
@ComponentV2
struct Index {
// connect 拿应用级可观察对象,key='appConfig',默认 new AppConfig()
appCfg: AppConfig = AppStorageV2.connect(AppConfig, 'appConfig', () => new AppConfig()) ?? new AppConfig()
@Local logText: string = '(未操作)'
@Local opCount: number = 0
build() {
// ...
}
}
connect 三个关键点:
① connect(type, key, default) 三参
typescript
AppStorageV2.connect(
AppConfig, // type: class 类型
'appConfig', // key: 应用级键名
() => new AppConfig() // defaultCreator: 默认值工厂
)
type:class 类型,告诉 AppStorageV2 存什么key:应用级键名,跨组件用同一个 key 拿同一个对象defaultCreator:第一次 connect 时用这个工厂新建默认值
② connect 返回 T | undefined
typescript
this.appCfg = AppStorageV2.connect(AppConfig, 'appConfig', () => new AppConfig()) ?? new AppConfig()
connect 可能返回 undefined(极端情况),用 ?? new AppConfig() 兜底。
③ 跨组件用同一个 key 拿同一个对象
typescript
// 组件 A
appCfg = AppStorageV2.connect(AppConfig, 'appConfig', () => new AppConfig())
// 组件 B
appCfg = AppStorageV2.connect(AppConfig, 'appConfig', () => new AppConfig())
// ← A 和 B 拿到的是同一个对象,A 改 B 自动同步
这是 AppStorageV2 最核心的能力------跨组件共享同一个可观察对象,A 改成员 B 自动重绘。
2.3 改成员 → UI 真重绘
typescript
Button('改 theme = dark(@Trace 触发重绘)')
.width('92%').height(44).fontSize(14)
.onClick(() => {
this.appCfg.theme = 'dark' // ← 改成员,UI 重绘
this.opCount++
this.logText = `第 ${this.opCount} 次:theme=dark,UI 重绘`
})
这是 V2 最核心的能力 ------改 this.appCfg.theme(应用级可观察对象成员)就真触发 UI 重绘。不用像 V1 那样 AppStorage.set + 手动同步。
2.4 remove / keys 管理应用级状态
typescript
Button('remove appConfig(AppStorageV2.remove)')
.onClick(() => {
AppStorageV2.remove('appConfig') // ← 删应用级键
})
Button('keys() 看所有应用级键')
.onClick(() => {
this.logText = `keys=${JSON.stringify(AppStorageV2.keys())}` // ← 列所有键
})
remove 删应用级键,keys 列所有应用级键------管理用。
三、真机实拍:改成员真触发 UI 重绘
我把这个 demo 装到真机上跑(鸿蒙 6.1.1.125, API 24),依次点改 theme=dark + 改 lang=en,下面两张都是真机实拍,没有任何 P 图。
初始态:应用级状态(theme=light, lang=zh, opCount=0)+ 5 个按钮:

点改 theme=dark + 改 lang=en 后:theme=dark, lang=en, opCount=2, 日志「第 2 次:lang=en,UI 重绘」:

重点看第二张:theme 从 light 变成 dark、lang 从 zh 变成 en、opCount 从 0 变成 2------改应用级可观察对象成员就真触发了 UI 重绘,不用手动同步 。这是
AppStorageV2.connect+@ObservedV2+@Trace的真机证明。
四、V2 AppStorageV2 vs V1 AppStorage:啥差异
新手最容易纠结的问题:既然 V1 AppStorage 那么标准,鸿蒙为啥要造 V2?
| 维度 | V1 AppStorage |
V2 AppStorageV2 |
|---|---|---|
| import | 全局名 | import { AppStorageV2 } from '@ohos.arkui.StateManagement' |
| 存取 API | set/get 键值对 |
connect 拿可观察对象 |
| 改成员触发重绘 | 不触发(要手动同步) | 触发(@Trace 装的成员) |
| 跨组件同步 | @StorageLink / @StorageProp |
同 key connect 同对象 |
| 配套装饰 | @Component + @State |
@ComponentV2 + @Local |
| 类型安全 | 弱(any) | 强(class 类型) |
一句话决策:新项目鸿蒙 6.1+ 用 V2,改成员就重绘不用手动同步;V1 项目维护老代码不动。
五、常见坑(都是血泪)
| 坑 | 症状 | 解法 |
|---|---|---|
AppStorageV2.set/get |
编译报「没有 set 方法」 | V2 没有 set/get,用 connect 拿可观察对象 |
忘 import AppStorageV2 |
编译报「Cannot find name AppStorageV2」 | import { AppStorageV2 } from '@ohos.arkui.StateManagement' |
connect 忘装 @ObservedV2 class |
改成员不触发重绘 | class 必须 @ObservedV2 + 成员 @Trace |
connect 返回 undefined 没兜底 |
运行时 null 引用 | ?? new AppConfig() 兜底 |
| 跨组件 key 不一致 | A 改 B 不同步 | 跨组件用同一个 key |
用在 V1 @Component 里 |
编译报「V2 装饰器只能装在 @ComponentV2」 | struct 改 @ComponentV2 |
忘 remove 清理 |
应用级状态泄漏 | 不用了调 AppStorageV2.remove(key) |
六、PersistenceV2:持久化版应用级状态
鸿蒙 6.1 还给了 PersistenceV2------AppStorageV2 的持久化版,应用重启后状态还在:
typescript
import { PersistenceV2 } from '@ohos.arkui.StateManagement'
// PersistenceV2.globalConnect(...) 持久化版 connect
// PersistenceV2.save(key) 主动保存到磁盘
| API | 用途 | 与 AppStorageV2 的区别 |
|---|---|---|
AppStorageV2.connect |
应用生命周期内共享 | 重启后丢 |
PersistenceV2.globalConnect |
持久化到磁盘 | 重启后还在 |
PersistenceV2.save |
主动保存 | 写磁盘 |
场景决策 :主题/语言这种用户设置用 PersistenceV2(重启还在),临时状态用 AppStorageV2(重启丢)。
七、完整代码仓库
本文所有代码都已托管到 AtomGit,欢迎 clone、提 issue、点 star:
🔗 仓库地址 :atomgit.com/JaneConan/a...
仓库包含:
- 完整的「AppStorageV2 应用级主题/语言切换」demo 工程
Index.ets主页面(import AppStorageV2+@ObservedV2class +connect+ 改成员重绘 +remove/keys)- 跨组件共享同 key 的姿势说明
- 可直接用 DevEco Studio 打开运行(真机装普通应用必能跑)
八、下一步该学什么?
跑通这个 demo 之后,你的 V2 应用级状态就入门了。这是 V2 装饰器系列第二篇,后续按这个顺序往下:
@Param+@Once(下一篇):V2 父子传参,替代 V1@Prop+@Link@Provider+@Consumer:V2 跨层传递,替代 V1@Provide+@Consume@Monitor:V2 监听变化,替代 V1@Watch@Computed:V2 计算属性,自动派生PersistenceV2:持久化版应用级状态,重启还在
写在最后
AppStorageV2 的本质,是**「鸿蒙 6.1 给应用级状态 connect 拿可观察对象的原生机制」**------不是 V1 的「set/get 键值对」,是 V2 专门给精细追踪的原生机制。代价是 import 多一行、connect 三参多一步。
一旦你开始用 V2 思维写应用级状态,你会发现大部分「主题切换」「语言切换」「用户信息共享」「全局计数器」的需求,都是 AppStorageV2.connect + @ObservedV2 + @Trace 的自然结果。代码量比 V1 手动同步少一半,精细度高一倍。
代码已经给你了,仓库链接在上面。现在关掉这篇文章,打开 DevEco Studio,把 demo 跑起来,亲手点改 theme 改 lang 感受下应用级状态自动同步。
跑通了,回来评论区打个「1」,我看看有多少人真的动手了。🚀
作者:JaneConan 仓库:atomgit.com/JaneConan/a... 协议:Apache-2.0,随便用,别告我