尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

HarmonyOS 「星办OA」App应用实战14 : @ObservedV2/@Trace 深度观察

HarmonyOS 「星办OA」App应用实战14 : @ObservedV2/@Trace 深度观察 ObservedV2/Trace 深度观察一、引言在 HarmonyOS NEXT 的 ArkUI 框架中ObservedV2和Trace装饰器构成了深层对象观察机制的核心。它们允许框架精确追踪对象的属性级变化实现高效的响应式更新。本文将以星办 OA企业办公审批项目中的ApprovalStore.ets为实际案例深入解析ObservedV2和Trace的原理、用法以及性能优化策略。二、ObservedV2/Trace 的基本概念2.1 为什么需要深层观察在 ArkUI 的状态管理中Local装饰器只能追踪变量的引用变化即重新赋值而无法追踪对象内部属性的变化。例如Local user: UserProfile new UserProfile() // 以下操作不会触发 UI 更新 this.user.name 新名字这是因为 JavaScript/TypeScript 引擎无法自动检测对象属性的变化。为了解决这个问题ArkUI 提供了ObservedV2和Trace装饰器实现对对象内部属性的深度观察。2.2 ObservedV2 和 Trace 的职责ObservedV2装饰在类上表示该类需要被深度观察。被装饰的类会成为一个可观察对象。Trace装饰在类的属性上表示该属性需要被追踪。当属性的值发生变化时框架会通知依赖该属性的组件进行更新。2.3 基本用法ObservedV2 export class ObservableClass { Trace name: string Trace age: number 0 }三、ApprovalStore 中的实际应用3.1 ApprovalStore 类的定义在星办 OA项目中ApprovalStore是核心的数据存储类它使用了ObservedV2和Trace装饰器// 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() // ... }这里ObservedV2装饰了整个ApprovalStore类Trace装饰了三个属性approvals审批请求列表messages消息列表profile员工档案3.2 Trace 对数组的追踪Trace装饰数组属性时可以追踪数组的引用变化。在ApprovalStore中对数组的修改都通过引用替换来实现// 提交审批 — 创建新数组 submit(type: string, title: string, summary: string, reason: string): ApprovalMutation { // ... let result: ApprovalMutation submitApproval(this.approvals, input, id, 刚刚) if (result.success) { this.approvals result.approvals // 引用替换触发 UI 更新 this.addActionMessage(result, 申请已提交, ${type}申请已进入审批流程。) } return result }当this.approvals result.approvals执行时由于approvals被Trace装饰框架会检测到引用变化并通知所有依赖approvals的组件更新。3.3 Trace 对对象的追踪Trace不仅可以追踪基本类型还可以追踪对象类型。profile是一个EmployeeProfile对象Trace profile: EmployeeProfile new EmployeeProfile()EmployeeProfile类虽然没有被ObservedV2装饰但由于profile属性本身被Trace装饰当profile被整个替换时框架会检测到变化。3.4 Type 装饰器的作用在ApprovalStore中我们还看到了Type装饰器的使用Type(ApprovalRequest) Trace approvals: ApprovalRequest[] createDemoApprovals() Type(ApprovalMessage) Trace messages: ApprovalMessage[] createDemoMessages()Type装饰器用于指定数组中元素的类型帮助框架在运行时正确处理数组元素的可观察性。当数组中包含ApprovalRequest或ApprovalMessage类型的对象时Type确保框架能够正确追踪这些对象的属性变化。四、深层对象观察的实现原理4.1 属性代理机制ObservedV2装饰器在运行时会将类实例的Trace属性替换为 getter/setter 代理。当属性被读取时当前正在执行的渲染函数会被注册为依赖当属性被写入时所有依赖该属性的渲染函数会被标记为需要重新执行。4.2 依赖收集与通知依赖收集的过程如下组件渲染阶段当build()或Builder方法执行时框架会创建一个渲染上下文属性读取当渲染上下文中读取了某个Trace属性的值时该渲染上下文会被注册为该属性的依赖属性变化当Trace属性的值发生变化时框架会遍历所有依赖该属性的渲染上下文并标记它们为脏重新渲染在下一帧渲染时所有被标记为脏的渲染上下文会重新执行4.3 数组追踪的特殊处理数组的追踪比普通对象更复杂因为数组有以下操作直接赋值this.approvals newArray元素修改this.approvals[0] newItem数组方法push、pop、splice、forEach等在星办 OA项目中可以看到对数组的修改都遵循引用替换模式// 直接替换整个数组 — 触发 UI 更新 this.approvals result.approvals this.messages [item, ...this.messages] // 通过展开运算符创建新数组 — 触发 UI 更新 this.messages [...this.messages] // 在数组元素中修改属性后需要重新赋值触发更新 markMessageRead(id: string): void { let changed: boolean false this.messages.forEach((item: ApprovalMessage) { if (item.id id !item.isRead) { item.isRead true changed true } }) if (changed) { this.messages [...this.messages] // 引用替换触发 UI 更新 } }这里的关键是即使修改了数组中元素的属性item.isRead true也不会自动触发 UI 更新。必须通过this.messages [...this.messages]来替换整个数组的引用框架才能检测到变化。五、ObservedV2/Trace 与 Observed/ObjectLink 的对比5.1 功能对比特性Observed/ObjectLink (V1)ObservedV2/Trace (V2)装饰位置类 属性类 属性属性级追踪不支持对象级支持属性级数组追踪需要特殊处理引用替换即可性能全量比较精确通知使用复杂度较高较低5.2 性能优势V2 版本的Trace实现了属性级别的变化追踪这意味着当approvals变化时只有依赖approvals的组件会重新渲染当messages变化时只有依赖messages的组件会重新渲染当profile变化时只有依赖profile的组件会重新渲染而在 V1 版本中Observed装饰的对象发生任何变化所有引用该对象的组件都会重新渲染导致不必要的性能开销。六、组件与 ObservedV2 的协作6.1 组件中连接数据源在星办 OA的各个页面组件中通过AppStorageV2.connect连接ApprovalStore// HomePage ComponentV2 export struct HomePage { Local store: ApprovalStore AppStorageV2.connectApprovalStore(ApprovalStore, () new ApprovalStore())! // ... }AppStorageV2.connect返回的是一个可观察的ApprovalStore实例。当store被Local装饰后组件就建立起了与数据存储的响应式连接。6.2 响应式数据流以审批列表的渲染为例数据流如下用户操作 → ApprovalStore 方法调用 → Trace 属性变化 → 框架检测到变化 → 组件重新渲染 → UI 更新具体到OfficePage的审批列表用户点击同意→ ApprovalStore.approve() → this.approvals result.approvals (Trace 属性变化) → 框架通知 HomePage、OfficePage 等组件 → getVisibleApprovals() 重新计算 → buildApprovalList() 重新渲染 → 列表更新6.3 多层嵌套的对象观察对于多层嵌套的对象ObservedV2和Trace可以逐层装饰。在CommonInterface.ets中ObservedV2 export class Suggestion { Trace date: Date new Date() Trace title: string Trace image?: string[] [] } ObservedV2 export class SuggestionList { Type(Suggestion) Trace suggestion: Suggestion[] [] }这里SuggestionList包含Trace suggestion数组数组中的元素类型是Suggestion类也被ObservedV2装饰从而实现了多层嵌套的深度观察。七、性能优化策略7.1 避免过度追踪不是所有属性都需要被Trace装饰。只有那些会被 UI 读取且会变化的属性才需要被追踪。在ApprovalStore中ObservedV2 export class ApprovalStore { Trace approvals: ApprovalRequest[] createDemoApprovals() Trace messages: ApprovalMessage[] createDemoMessages() Trace profile: EmployeeProfile new EmployeeProfile() // 方法和计算属性不需要 Trace getStats(): DashboardStats { ... } getPendingApprovals(): ApprovalRequest[] { ... } }getStats()和getPendingApprovals()是普通方法没有被Trace装饰因为它们不存储状态只进行计算。7.2 批量更新当需要同时修改多个属性时应该尽量在一次操作中完成避免触发多次渲染。在ApprovalStore的approve方法中approve(id: string, comment: string): ApprovalMutation { let result: ApprovalMutation approveApproval(this.approvals, id, comment, 刚刚) if (result.success) { this.approvals result.approvals // 一次赋值 this.messages resolvePendingMessages(this.messages, id, result.action) // 一次赋值 this.addActionMessage(result, 审批操作已完成, result.message) // 内部一次赋值 } return result }虽然这里有三处赋值操作但由于 JavaScript 的事件循环机制它们会在同一个微任务中完成最终只会触发一次渲染。7.3 数组操作的优化在数组操作中尽量减少不必要的引用替换// 好的做法只在真正变化时才替换 markMessageRead(id: string): void { let changed: boolean false this.messages.forEach((item: ApprovalMessage) { if (item.id id !item.isRead) { item.isRead true changed true } }) if (changed) { this.messages [...this.messages] // 只在有变化时触发更新 } }这里通过changed标志位来判断是否真的发生了变化避免了不必要的 UI 更新。八、最佳实践8.1 设计可观察的数据模型在星办 OA项目中数据模型的设计遵循以下原则集中管理将相关的数据放在同一个ObservedV2类中如ApprovalStore按需追踪只对 UI 关心的属性使用Trace类型标注使用Type装饰器标注数组元素类型确保深层追踪正确8.2 组件与数据分离ObservedV2类数据模型与ComponentV2结构体组件分离职责清晰数据模型负责数据的管理和业务逻辑组件负责 UI 的渲染和用户交互数据模型通过Trace属性与组件建立响应式连接8.3 避免副作用在Trace属性的 getter 中避免执行有副作用的操作因为 getter 可能被频繁调用。在ApprovalStore中所有计算逻辑都放在普通方法中ObservedV2 export class ApprovalStore { Trace approvals: ApprovalRequest[] createDemoApprovals() // 计算逻辑放在方法中而不是 getter 中 getStats(): DashboardStats { return getDashboardStats(this.approvals, this.messages) } }九、总结ObservedV2和Trace装饰器是 ArkUI 框架中深层对象观察的核心机制。通过星办 OA项目中ApprovalStore.ets的实际代码分析我们深入理解了这些装饰器的工作原理和最佳实践。ObservedV2装饰类Trace装饰属性两者配合实现了属性级别的精确变化追踪。与 V1 版本的Observed/ObjectLink相比V2 版本提供了更好的性能和更简单的使用方式。在实际开发中合理设计数据模型、正确使用Trace装饰器、遵循引用替换的数组操作模式是构建高性能、响应式 HarmonyOS NEXT 应用的关键。
返回列表