在 ArkUI 的状态管理中,@Observed 与 @ObjectLink 是处理嵌套对象或对象数组的核心武器。它们解决了 @State 无法深度观察对象属性变化的局限性。
1. @Observed 与 @ObjectLink 的底层区别
这两者必须成对出现,它们的职责完全不同:
@Observed:类装饰器(观察能力的赋予)
- 底层行为:它是一个编译插件。当一个类被
@Observed装饰时,ArkTS 编译器会重写该类的setter和getter。 - 职责:它让类具备了“被观察”的能力。每当你修改类中的成员变量时,它会触发一个内部的
notify信号。 - 限制:仅靠
@Observed是无法刷新 UI 的,它只是一个“信号发射源”。
@ObjectLink:变量装饰器(状态的订阅与代理)
- 底层行为:它建立了一个指向 @Observed 实例的代理(Proxy) 。
- 职责:它充当“信号接收器”。当子组件接收到父组件传来的
@Observed实例时,@ObjectLink会将子组件注册为该实例的观察者。 - 同步机制:它是双向同步的。在子组件内修改属性,直接作用于原始对象实例,并驱动所有链接该实例的组件重绘。
2. 大对象频繁更新带来的性能问题
如果你将一个拥有 100 个字段的大对象定义为 @Observed,并频繁更新其中一个字段,会面临以下压力:
- Proxy 拦截开销:所有的属性访问都会经过代理层的拦截逻辑。虽然单次很快,但高频(如动画中每秒 120 次)更新会累积 CPU 耗时。
- 冗余重绘(Over-rendering) :如果多个组件
@ObjectLink了同一个大对象,即使你只改了A字段,所有引用了该对象的组件都会被触发“潜在更新检查”。 - 深拷贝与内存抖动:在某些复杂的传递场景下,框架为了保证状态一致性可能会产生临时闭包或引用记录,频繁操作大对象会增加垃圾回收(GC)的频率。
3. 如何优化深层状态更新?
针对“深层”和“高频”,可以采用以下三种进阶策略:
A. 状态扁平化 (State Flattening)
核心思想:不要嵌套。将深层属性提取到顶层,或者将大对象拆分为多个独立的小对象。
- 效果:缩短观察链路,减少受影响的组件范围。
B. 局部化更新 (Localization)
不要在父组件中处理所有逻辑。
- 做法:将渲染逻辑下放到子组件。让子组件通过
@ObjectLink只观察它关心的那部分数据。 - 原理:这样当属性变化时,父组件的
build()不会重新执行,仅子组件局部刷新。
C. 按需更新与 @Track
在 HarmonyOS 的最新版本中,引入了 @Track 装饰器。
- 用法:在类成员上使用
@Track。 - 机制:未标记
@Track的属性变化不会触发 UI 刷新。这实现了属性级别的精准控制,彻底解决了“修改 A 导致引用了 B 的组件也刷新”的问题。
TypeScript
@Observed
class User {
@Track name: string; // 只有修改 name 才会触发关联 UI 刷新
age: number; // 修改 age 不会触发 UI 刷新
}
D. 批量更新 (TaskPool/Emitter)
对于极高频的数据(如传感器、金融秒级行情):
- 做法:不要直接改状态变量。在后台线程处理好数据,通过
Emitter定时(如每 100ms)批量同步一次到 UI 状态。
总结
- @Observed 是“发报机”, @ObjectLink 是“收音机”。
- 大对象会导致“一人感冒,全家吃药”(冗余刷新),应尽量拆分。
- 优化首选:使用
@Track进行属性级过滤,或通过组件拆分实现局部刷新。