@State 与 @Observed:HarmonyOS 状态管理到底怎么选

前言

状态管理是 ArkUI 开发的地基。很多开发者分不清 @State@Observed 的边界,结果"对象里的字段改了界面不刷新",或"为了一个嵌套属性把整个对象都标记响应式"导致性能浪费。本文基于论坛问题「@State 与 @Observed 在状态管理上该如何」,把二者的职责边界和组合用法讲透。

问题描述

  • @State 装了一个对象,修改 obj.field 后界面没刷新;
  • 嵌套对象(对象里的对象)属性变化无法被感知;
  • 子组件想观察父传来的对象内部变化,不知道用什么装饰器;
  • 听说 @Observed 很强大,于是到处用,反而拖慢渲染。

细节解析

1. @State:组件私有状态,只认"整体替换"

@State 管理组件自身的状态。@State obj: MyObj 只有在 obj 这个引用被整体替换时才触发刷新;修改 obj.a.b 这种深层字段,框架并不知道,因此不刷新。它适合基础类型或"整块数据换新"的场景。

2. @Observed + @ObjectLink:观察嵌套对象内部

要让嵌套对象内部属性的变化也驱动 UI,需要:

  • @Observed 装饰那个 class(可多层嵌套,每层都要标);
  • 在消费该对象的子组件里用 @ObjectLink 接收。

@ObjectLink 指向的是父组件里 @State/@Link 持有的同一个对象引用,因此对象内部字段变化能精确通知到用了它的子组件。

3. V1 与 V2 的对应关系

  • V1:@State + @Observed + @ObjectLink(本文示例);
  • V2:@Local + @ObservedV2 + @Trace(更细粒度,只刷新被 @Trace 标记的属性)。

4. 选择原则

  • 基础类型 / 整体替换的对象 → @State(V2 用 @Local);
  • 需要观察对象内部属性、且跨多组件共享该对象 → @Observed + @ObjectLink
  • 不要为了"能用"就给所有对象套 @Observed,只在确实要观察内部变化时才用。

示例代码(V1)

@Observed
class Address {
  city: string = 'Beijing';
  street: string = 'Main St';
}
@Observed
class User {
  name: string = 'Tom';
  addr: Address = new Address();   // 嵌套对象也要 @Observed
}

// 父组件
@Entry @Component
struct Parent {
  @State user: User = new User();
  build() {
    Column() {
      Button('改城市').onClick(() => { this.user.addr.city = 'Shanghai'; })
      Child({ user: this.user })
    }
  }
}

// 子组件:用 @ObjectLink 观察同一对象引用
@Component
struct Child {
  @ObjectLink user: User;
  build() {
    Column() {
      Text('姓名:' + this.user.name)
      Text('城市:' + this.user.addr.city)  // 父改 city 这里会刷新
    }
  }
}

总结

  • @State 管"整体换",@Observed+@ObjectLink 管"内部变";
  • 嵌套对象每一层都要 @Observed,否则深层不刷新;
  • obj.field 不刷新 → 正是 @State 的边界,换 @Observed 组合即可;
  • V2 用 @Local/@ObservedV2/@Trace 实现更精准的属性级刷新。

选对装饰器,既能保证刷新正确,又能避免无谓的性能开销。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐