2-18.【ArkTS】@Observed 与 @ObjectLink 的区别?大对象监听是否有性能问题?如何避免深层 diff?

1 阅读3分钟

在 ArkUI 的状态管理体系中,@Observed@ObjectLink 是处理嵌套对象对象数组的核心武器。它们解决了 @State 只能监听到对象“第一层”变化的局限。


1. @Observed 与 @ObjectLink 的核心区别

简单来说,这是一对**“母子”**关系:@Observed 作用于类,@ObjectLink 作用于子组件的变量。

维度@Observed@ObjectLink
装饰对象类 (Class)子组件内的变量
底层功能赋予类“可被观察”的能力。编译器会给类的属性加上 Getter/Setter 拦截。建立一个“实时连接”。它接收一个被 @Observed 类实例的引用。
同步方向定义数据源。双向同步。子组件改了属性,父组件和所有链接处同步刷新。
使用限制只能装饰 Class,不能装饰 Interface 或内置类型。必须配合子组件使用,且参数必须是 @Observed 修饰的实例。

2. 大对象监听是否有性能问题?

答案是:如果不加节制地使用,会有显著的性能开销。

性能损耗的来源:

  1. 内存开销: @Observed 会在编译阶段为类的每一个属性生成代理(Proxy-like)逻辑。如果你的对象有上百个字段,每个实例都会携带大量的拦截器元数据。
  2. 监听负担: 每一个 @ObjectLink 都会向数据源注册一个订阅者。在大对象数组(如 1000 条数据的列表)中,管理这些订阅关系会消耗 CPU 资源。
  3. 深层遍历: 虽然 ArkTS 优化了 Diff,但如果你在 build() 中引用了一个极其复杂的嵌套对象,状态改变时框架仍需解析相关的引用路径。

3. 如何避免深层 Diff?

在 Web 开发(如 React)中,深层 Diff 是通过“浅比较”或“不可变数据(Immutable)”解决的。在 ArkTS 中,我们有更符合原生逻辑的方案:

A. 拆分数据结构(扁平化)

不要设计“祖孙三代”的对象结构。

  • user.post.comment.author.name

  • ✅ 将 comments 抽离为独立的列表,通过 ID 关联。

    原理: 减少单个 @Observed 类的属性数量,让更新只影响最小范围。

B. 使用“按需监听” (Object Ref vs. Property)

如果你只需要显示姓名,不要把整个巨大的 User 对象传给子组件。

  • 优化: 在子组件中只接收 name: string
  • 结果: 只有当 name 改变时,该子组件才会触发 Diff,对象的其他字段变动不会干扰这个组件。

C. 配合 LazyForEach 缓存

在大对象列表场景下,务必配合 LazyForEach

  • LazyForEachkeyGenerator 应该基于对象的唯一 ID 而非索引。
  • 这样当大对象中的某个属性变化时,ArkUI 只会精确 Diff 对应的 Item 节点,而不会扫描整个列表。

4. 最佳实践:什么时候用?

  1. 对象数组: 列表里的每一项都是一个对象,且点击某一项要修改其内容(如点赞、改名),必须用 @Observed + @ObjectLink
  2. 层级嵌套: 只有当你需要观察 obj.subObj.prop 时才使用。
  3. 性能瓶颈: 如果发现滑动卡顿,检查是否在 build() 里对 @Observed 对象做了大量的 Array.mapfilter 等计算逻辑。