在这里插入图片描述

网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括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。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐