鸿蒙 App 状态管理:实现原理 + 最佳实践
本文由华为鸿蒙开发者展菲撰写,深入解析了ArkUI状态管理的核心机制与实践要点。文章首先指出新手常见的变量更新误区,强调鸿蒙采用"状态驱动UI"的设计理念。通过分析@State的工作原理,揭示了状态变更触发的自动刷新机制。针对项目规模扩大后的状态管理挑战,提出分层架构方案(LocalState/PageState/GlobalState/DistributedState),并展示了UserStor

大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。
图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG
我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。
展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
文章目录
引言
很多开发者刚开始做鸿蒙 App 时,对状态管理的理解其实非常简单。
例如:
点击按钮
↓
修改变量
↓
刷新页面
看起来没什么问题,甚至很多 Demo 里都是这样:
@Entry
@Component
struct DemoPage {
private count: number = 0
build() {
Column() {
Text(`${this.count}`)
Button('点击')
.onClick(() => {
this.count++
})
}
}
}
结果运行以后发现:
页面根本不会刷新
于是很多人第一次接触 ArkUI 时都会疑惑:
变量变了
为什么 UI 不变?
因为在鸿蒙里:
UI 从来不是由变量驱动的。
而是驱动:
State
真正的大型项目里:
状态
=
应用运行时世界
而不是:
几个变量
这也是鸿蒙状态管理的核心思想。
一、为什么状态管理比你想象的重要
很多开发者理解 App:
页面
↓
接口
↓
数据显示
但系统真正运行时其实是:
Server
↓
State
↓
UI
例如一个在线学习 App:用户进入课程页面。
页面显示:
- 用户信息
- 学习进度
- 视频播放状态
- 收藏状态
- AI 助教状态
这些内容本质上都是:
State
UI 只是状态的一层展示,例如:
interface CourseState {
courseId: string
progress: number
isFavorite: boolean
}
页面真正依赖的是:
courseState
而不是:
Text()
Button()
Column()
二、ArkUI 的核心:State Driven UI
ArkUI 的设计思想非常明确:
State
↓
Render
↓
UI
例如:
@Entry
@Component
struct Counter {
@State count: number = 0
build() {
Column() {
Text(`${this.count}`)
Button("增加")
.onClick(() => {
this.count++
})
}
}
}
这里:
this.count++
发生以后:
count变化
↓
ArkUI监听
↓
组件Dirty
↓
局部重建
↓
UI更新
开发者不用主动刷新页面,ArkUI 自动完成。这也是响应式编程最大的优势。
三、@State 的底层工作机制
很多人以为:
@State count = 0
只是语法糖,实际上:
@State
会把变量注册到状态观察系统,例如:
@State userName: string = "Tom"
当执行:
this.userName = "Jack"
ArkUI 会自动记录:
userName发生变化
然后标记对应组件:
Dirty
最后执行:
局部刷新
而不是:
整个页面重绘
这也是 ArkUI 性能高的重要原因。
四、第一个大坑:状态和普通变量混用
很多项目里经常出现:
@State count = 0
private loading = false
然后:
this.loading = true
开发者发现:
UI没更新
原因很简单:
loading
不是状态
例如:
@State loading = false
Button("加载")
.onClick(() => {
this.loading = true
})
这样页面才会刷新,经验原则:
参与UI渲染
必须是State
五、为什么大型项目必须引入 Store
项目初期:
@State userName = "Tom"
没有问题,但项目越来越大以后:
首页
课程页
订单页
个人中心
AI助手
都需要访问用户信息,于是出现:
Props地狱
Page
↓
ComponentA
↓
ComponentB
↓
ComponentC
每层都要传:
userInfo
维护成本极高,所以大型项目都会引入:
Store
六、构建 UserStore
例如:
@Observed
export class UserStore {
userId: string = ''
userName: string = ''
avatar: string = ''
updateUser(name: string) {
this.userName = name
}
}
创建全局实例:
export const userStore = new UserStore()
页面直接读取:
@Entry
@Component
struct ProfilePage {
@ObjectLink
user = userStore
build() {
Column() {
Text(this.user.userName)
Button("修改昵称")
.onClick(() => {
this.user.updateUser("Harmony")
})
}
}
}
这时候:
Store变化
↓
所有依赖页面同步刷新
七、推荐的状态分层架构
大型项目推荐:
State
├── LocalState
├── PageState
├── GlobalState
└── DistributedState
LocalState
组件内部状态:
@State expanded = false
例如:
Button("展开")
.onClick(() => {
this.expanded = !this.expanded
})
适用于:
- Tab
- 折叠面板
- 弹窗
PageState
页面级状态:
class CoursePageState {
currentChapter: number = 1
progress: number = 0
}
例如:
coursePageState.progress = 60
仅当前页面使用。
GlobalState
全局状态,全局共享:
class UserStore {
}
class MessageStore {
}
class AuthStore {
}
DistributedState
鸿蒙特有,例如:
interface DeviceState {
deviceId: string
workspaceId: string
taskId: string
}
用于:
- 跨设备协同
- Workspace同步
- AI任务迁移
八、为什么滥用 @Link 很危险
很多团队为了方便:
@Link userName: string
到处使用,例如:
PageA
↕
PageB
↕
PageC
所有组件共享同一个状态,后期经常出现:
用户昵称突然变化
但没人知道是谁改的,推荐:
默认Prop
必要时Link
例如:
@Prop userName: string
优先单向数据流。
九、AI App 带来的新挑战
传统 App:
用户状态
订单状态
就够了。
AI App 完全不同,例如:
interface AIContext {
sessionId: string
messages: Message[]
tools: ToolState[]
memory: MemoryState[]
}
用户发送消息:
aiStore.messages.push(message)
不仅影响:
聊天窗口
还会影响:
Agent
Tool
Memory
整个运行时,所以未来最复杂的状态往往是:
AI Runtime State
十、状态持久化设计
很多项目:
退出App
↓
状态丢失
体验很差,推荐:
State
↓
Snapshot
↓
Storage
例如:
class PersistenceManager {
save(state: any) {
AppStorage.SetOrCreate(
"app_state",
JSON.stringify(state)
)
}
restore() {
return JSON.parse(
AppStorage.Get("app_state")
)
}
}
启动时:
const snapshot =
PersistenceManager.restore()
恢复:
用户状态
课程状态
AI状态
十一、鸿蒙跨设备状态同步实践
例如,用户正在手机编辑文档:
interface DocumentState {
docId: string
cursor: number
content: string
}
拖到鸿蒙 PC,真正同步的是:
{
docId: "001",
cursor: 120,
content: "..."
}
而不是:
整个页面
恢复:
documentStore.restore(snapshot)
实现:
工作连续性
这也是鸿蒙分布式能力的核心价值。
十二、性能优化案例
很多项目:
@State appState = {
user:{},
ai:{},
course:{},
message:{}
}
任何字段变化:
全部刷新
性能迅速下降,正确做法:
UserStore
CourseStore
AIStore
MessageStore
例如:
class AIStore {}
class UserStore {}
这样:
AI变化
↓
只刷新AI模块
不会影响整个页面。
十三、未来趋势:状态管理正在变成 Runtime 管理
过去:
管理数据
未来:
管理运行时
尤其 AI Native App 出现以后,状态不再只是:
用户信息
而是:
Agent状态
Memory状态
Workspace状态
Tool状态
推理状态
例如:
interface RuntimeState {
user: UserState
workspace: WorkspaceState
ai: AIContext
memory: MemoryState
}
最终:
State
=
Runtime
十四、推荐项目结构
state/
├── user/
│ ├── UserStore.ts
│ └── UserAction.ts
│
├── course/
│ ├── CourseStore.ts
│ └── CourseAction.ts
│
├── ai/
│ ├── AIStore.ts
│ ├── SessionStore.ts
│ └── MemoryStore.ts
│
├── distributed/
│ ├── DeviceStore.ts
│ ├── WorkspaceStore.ts
│ └── RuntimeStore.ts
│
└── persistence/
├── Snapshot.ts
└── Recovery.ts
核心原则:
UI 不管理状态
而是:
UI 消费状态
总结
如果一句话总结鸿蒙 App 状态管理:
页面只是状态的投影。
真正重要的从来不是:
- 页面怎么刷新
- 组件怎么通信
而是:
状态如何组织
包括:
- Local State
- Global State
- AI State
- Distributed State
- Runtime State
很多团队做到后期都会发现:
App 越复杂
状态越重要
因为真正驱动鸿蒙应用运行的,不是页面,而是:
State Runtime
未来的鸿蒙 App,尤其是 AI Native App,管理的已经不只是数据。而是:
整个应用运行时世界
所以状态管理的终点,从来不是 Store。而是:
Runtime。
更多推荐




所有评论(0)