HarmonyOS 「星办OA」App应用实战32 : 企业应用中的状态管理架构:从全局到局部的分层设计
企业应用中的状态管理架构:从全局到局部的分层设计
引言
在星办 OA 这个企业级办公审批应用中,状态管理是架构设计的核心挑战之一。应用需要处理审批流程的状态流转、消息通知的已读/未读状态、用户信息的展示,以及多个页面之间的数据共享和同步。HarmonyOS NEXT 提供了丰富的状态管理机制,从全局的 AppStorageV2 到组件级别的 @Local,形成了一个完整的分层状态管理体系。

本文将深入分析星办 OA 项目的整体状态管理架构,探讨从全局状态到组件状态的分层设计、单向数据流模式,以及如何通过 ApprovalStore 实现集中式状态管理。
一、状态管理架构总览
1.1 三层状态模型
星办 OA 的状态管理架构可以分为三个层次:
┌─────────────────────────────────────────────┐
│ 第一层:全局状态(AppStorageV2) │
│ - ApprovalStore(审批数据) │
│ - pageInfos(导航栈) │
│ - topRectHeight / bottomRectHeight(设备参数)│
├─────────────────────────────────────────────┤
│ 第二层:页面状态(@Local) │
│ - tabCurrentIndex(Tab 选中索引) │
│ - selectedView(审批视图筛选) │
│ - selectedType(审批类型筛选) │
│ - keyword(搜索关键词) │
├─────────────────────────────────────────────┤
│ 第三层:组件状态(@Local / @Param) │
│ - comment(审批意见输入) │
│ - title/summary/reason(表单输入) │
│ - approvalNotificationEnabled(开关状态) │
└─────────────────────────────────────────────┘
这种分层设计遵循了以下原则:
- 全局共享的数据存储在
AppStorageV2中 - 页面相关的临时状态使用
@Local管理 - 组件内部状态使用
@Local或@Param接收父组件数据
1.2 核心状态管理类:ApprovalStore
ApprovalStore 是整个应用的状态中心,它集中管理了所有审批相关的数据:
// commons/common/src/main/ets/model/ApprovalStore.ets
@ObservedV2
export class ApprovalStore {
@Type(ApprovalRequest)
@Trace approvals: ApprovalRequest[] = createDemoApprovals()
@Type(ApprovalMessage)
@Trace messages: ApprovalMessage[] = createDemoMessages()
@Trace profile: EmployeeProfile = new EmployeeProfile()
}
ApprovalStore 的设计体现了以下特性:
- 单例模式:通过
AppStorageV2.connect确保全局只有一个实例 - 集中管理:所有审批数据、消息数据和用户信息集中在一个 Store 中
- 响应式:使用
@ObservedV2和@Trace实现响应式数据绑定 - 类型安全:使用
@Type标注复杂类型,确保运行时类型一致性
二、全局状态管理:AppStorageV2
2.1 AppStorageV2 连接模式
在星办 OA 项目中,每个页面组件都通过 AppStorageV2.connect 连接到全局的 ApprovalStore 实例:
// HomePage.ets
@Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(
ApprovalStore,
() => new ApprovalStore()
)!
// OfficePage.ets
@Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(
ApprovalStore,
() => new ApprovalStore()
)!
// InteractionPage.ets
@Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(
ApprovalStore,
() => new ApprovalStore()
)!
// MinePage.ets
@Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(
ApprovalStore,
() => new ApprovalStore()
)!
这种连接模式的特性:
- 延迟初始化:
() => new ApprovalStore()是一个工厂函数,只在首次连接时调用 - 自动同步:所有连接的组件共享同一个实例,数据自动同步
- 类型安全:泛型参数
ApprovalStore确保连接的类型正确 - 非空断言:
!断言连接一定成功
2.2 全局导航状态
除了数据状态,导航状态也通过全局机制管理。MainPage 使用 @Provider 提供导航栈:
// MainPage.ets
@Provider('pageInfos') pageInfos: NavPathStack = new NavPathStack()
其他页面通过 @Consumer 消费该导航栈:
// HomePage.ets
@Consumer('pageInfos') pageInfos: NavPathStack = new NavPathStack()
// OfficePage.ets
@Consumer('pageInfos') pageInfos: NavPathStack = new NavPathStack()
这种 @Provider/@Consumer 模式实现了跨组件的数据共享,而不需要将数据层层传递。
2.3 设备参数全局存储
应用还使用了 AppStorage(非 V2 版本)来存储设备参数:
// 在页面中使用
.padding({ top: Number(AppStorage.get('topRectHeight')) })
这些参数在应用启动时初始化,用于适配不同设备的屏幕区域(如状态栏高度、底部导航栏高度)。
三、页面状态管理:@Local
3.1 @Local 的作用域
@Local 装饰器用于管理页面级别的状态,这些状态只在当前页面及其子组件中可见。与全局状态不同,@Local 状态的生命周期与组件绑定。
在 HomePage 中:
@ComponentV2
export struct HomePage {
@Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(...)!
@Consumer('pageInfos') pageInfos: NavPathStack = new NavPathStack()
}
这里 store 虽然是 @Local 属性,但它的值是从全局 Store 连接而来的,因此 store.approvals 等属性的变化会自动同步到所有页面。
3.2 页面筛选状态的本地管理
在 OfficePage 中,多个筛选状态使用 @Local 管理:
@ComponentV2
export struct OfficePage {
@Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(...)!
@Local selectedView: string = ApprovalView.PENDING
@Local selectedType: string = '全部'
@Local keyword: string = ''
@Consumer('pageInfos') pageInfos: NavPathStack = new NavPathStack()
}
这些筛选状态是页面私有的,不需要与其他页面共享:
selectedView:当前选中的视图(待我审批/我发起的/已处理)selectedType:当前选中的审批类型筛选keyword:搜索关键词
@Local 状态发生变化,触发 UI 重新渲染,调用 getVisibleApprovals() 方法获取过滤后的数据。
3.3 表单状态的本地管理
在 ApprovalCreatePage 中,表单输入状态使用 @Local 管理:
@ComponentV2
export struct ApprovalCreatePage {
@Param approvalType: string = '请假'
@Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(...)!
@Local selectedType: string = '请假'
@Local title: string = ''
@Local summary: string = ''
@Local reason: string = ''
}
这些表单状态是典型的临时状态,只在表单填写过程中存在,提交后即被清除。
四、组件状态管理:@Param 与 @Event
4.1 父子组件数据传递
星办 OA 使用 @Param 和 @Event 实现父子组件之间的数据通信。以 CustomTabBar 为例:
// CustomTabBar.ets
@ComponentV2
export struct CustomTabBar {
@Param currentIndex: number = 0
@Event $currentIndex: (index: number) => void = () => {}
// ...
}
父组件 MainPage 使用 CustomTabBar:
// MainPage.ets
@Local tabCurrentIndex: number = 0
build() {
// ...
CustomTabBar({ currentIndex: this.tabCurrentIndex!! })
// ...
}
这种设计实现了:
- 数据向下传递:
tabCurrentIndex通过@Param传递给子组件 - 事件向上传递:子组件通过
@Event通知父组件状态变化
4.2 页面参数传递
对于页面级别的参数传递,星办 OA 使用 NavigationParams:
// CommonInterface.ets
export class NavigationParams {
title: string = ''
constructor(title: string) {
this.title = title
}
}
页面跳转时传递参数:
this.pageInfos.pushPathByName('ApprovalDetail', new NavigationParams(item.id))
目标页面通过 @Param 接收参数:
@ComponentV2
export struct ApprovalDetailPage {
@Param approvalId: string = ''
// ...
}
五、单向数据流模式
5.1 数据流方向
星办 OA 遵循单向数据流模式,数据流向始终是:
AppStorageV2 (全局状态) → @Local (页面状态) → @Param (组件状态)
状态的变更只能通过 Store 提供的公共方法进行:
用户操作 → Store 方法 → 状态变更 → UI 更新
5.2 在 ApprovalStore 中的体现
ApprovalStore 的所有公共方法都遵循不可变更新模式,确保数据流是单向且可预测的:
submit(type, title, summary, reason): ApprovalMutation {
let result = submitApproval(this.approvals, input, id, '刚刚')
if (result.success) {
this.approvals = result.approvals // 不可变更新
this.addActionMessage(result, '申请已提交', ...)
}
return result
}
approve(id, comment): ApprovalMutation {
let result = approveApproval(this.approvals, id, comment, '刚刚')
if (result.success) {
this.approvals = result.approvals // 不可变更新
this.messages = resolvePendingMessages(this.messages, id, result.action)
this.addActionMessage(result, '审批操作已完成', ...)
}
return result
}
每个方法都:
- 接收当前状态作为输入
- 调用纯函数处理业务逻辑
- 返回新的状态(不可变更新)
- 触发响应式更新
5.3 跨页面数据同步
由于所有页面连接到同一个 AppStorageV2 Store,当在一个页面中修改了数据,其他页面会自动同步更新:
- 用户在
ApprovalDetailPage中执行审批操作(同意/驳回) ApprovalStore.approve()或reject()方法被调用- 状态更新后,
HomePage中的待办列表自动刷新 InteractionPage中的消息列表自动更新MinePage中的统计数据自动变化
六、状态管理架构的设计原则
6.1 单一数据源
整个应用只有一个 ApprovalStore 实例,所有审批数据都从这个单一数据源获取。这避免了数据不一致的问题。
6.2 状态提升
当多个组件需要共享同一状态时,状态被提升到共同的父组件或全局 Store 中。例如,tabCurrentIndex 被提升到 MainPage,通过 @Provider 或 @Event 传递给子组件。
6.3 状态隔离
页面级别的筛选状态(selectedView、selectedType、keyword)使用 @Local 管理,不与其他页面共享,实现了状态隔离。
6.4 最小化全局状态
只有真正需要在多个页面间共享的数据才放入 AppStorageV2。临时状态(如表单输入、弹窗状态)保持在本地组件中。
七、实际数据流分析
7.1 审批操作的数据流
以用户在 ApprovalDetailPage 中点击"同意"按钮为例,数据流如下:
- 用户操作:点击"同意"按钮 →
confirmApprove()被调用 - 确认弹窗:
AlertDialog.show()显示确认对话框 - 执行操作:用户确认 →
this.store.approve(this.approval.id, this.comment)被调用 - Store 处理:
approve()方法调用approveApproval()纯函数 - 状态更新:
this.approvals = result.approvals触发响应式更新 - 消息同步:
this.messages = resolvePendingMessages(...)更新消息状态 - UI 更新:所有连接到 Store 的组件自动重新渲染
- 反馈提示:
promptAction.showToast({ message: result.message })显示操作结果
7.2 审批提交的数据流
用户在 ApprovalCreatePage 中提交申请:
- 表单验证:
confirmSubmit()检查必填字段 - 确认弹窗:显示确认对话框
- 执行提交:
this.store.submit(...)调用 Store 方法 - 创建审批:
submitApproval()创建新的ApprovalRequest实例 - 状态更新:
this.approvals = [item, ...approvals]在数组头部插入新元素 - 页面跳转:
replacePathByName跳转到审批详情页 - 全局同步:首页的待办统计和进行中列表自动更新
八、总结
星办 OA 的状态管理架构体现了企业级应用的最佳实践:
- 分层设计:全局状态(AppStorageV2)→ 页面状态(@Local)→ 组件状态(@Param),形成了清晰的状态层次
- 单向数据流:数据从上层向下层传递,事件从下层向上层传播,保证了数据流的可预测性
- 集中式 Store:通过
ApprovalStore实现集中式状态管理,所有数据变更通过 Store 的公共方法进行 - 响应式同步:
@ObservedV2/@Trace确保状态变更自动同步到所有 UI 组件 - 状态隔离:页面级别的临时状态使用
@Local管理,避免不必要的全局状态污染
这种架构设计使得星办 OA 应用具有良好的可维护性、可扩展性和性能表现,为构建复杂的企业级应用提供了可靠的架构基础。
更多推荐


所有评论(0)