HarmonyOS鸿蒙Next中修饰对象数组

HarmonyOS鸿蒙Next中修饰对象数组

如何解决使用@State修饰对象数组,数据变化时页面不刷新问题

5 回复

【背景知识】

【解决方案】

  1. @State装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组、对象数组,或者对象内嵌套对象,他们的第二层的属性变化是无法观察到的。例如:当装饰对象为对象数组时,可以观察到数组本身的赋值和添加、删除、更新数组的变化,但是数组项中属性的赋值观察不到。
  2. 如果想要实现动态渲染,可以使用@Observed/@ObjectLink装饰器:
    • @Observed用于对象数组、数组对象、嵌套对象场景中,观察对象类属性变化,用于修饰类。
    • @ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。注意:@ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。

@State装饰的变量可以观察到简单类型的变量值变化,在状态管理V1中要想观察到class中对象类型的某个属性的变化,可以使用@Observed/@ObjectLink装饰器。

可以参考demo:

  1. 使用@Observed修饰类:
[@Observed](/user/Observed)
 class Person {
   name:string;
   age:number;

   constructor(name:string, age:number) {
     this.name= name;
     this.age= age;
   }
 }
  1. 自定义子组件,注意@ObjectLink装饰器不能在@Entry装饰的自定义组件中使用:
@Component
struct MyPerson {
  [@ObjectLink](/user/ObjectLink) person: Person

  build() {
    Column() {
      Text(`姓名${this.person.name}`)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      Text(`年龄${this.person.age}`)
        .fontSize(15)
        .fontWeight(FontWeight.Regular)
    }
  }
}
  1. 在父组件中使用子组件:
[@Entry](/user/Entry)
@Component
struct PrePage {
  [@State](/user/State) persons: Person[] = [
    new Person('小张', 12),
    new Person('小赵', 13),
    new Person('小李', 14),
    new Person('小王', 15),
  ]

  build() {
    Scroll() {
      Column({ space: 10 }) {
        ForEach(this.persons, (curPerson: Person, index: number) => {
          MyPerson({ person: curPerson })
        })
        Button('修改对象数组属性-修改小张年龄为66')
          .onClick(() => {
            this.persons[0].age = 66
            console.info('当前小张的年龄为' + JSON.stringify(this.persons[0]))
          })
        Button('替换对象数组元素-修改小张年龄为88')
          .onClick(() => {
            this.persons.splice(0, 1, new Person('小张', 88))
            console.info('当前小张的年龄为' + JSON.stringify(this.persons[0]))
          })
      }
    }
    .height('100%')
    .width('100%')
  }
}

【总结】

@State装饰器仅能观察到第一层的变化。对于多层嵌套的情况,比如对象数组等,他们的第二层的属性变化是无法观察到的。@Observed装饰的类,可以观察到属性的变化;@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定

更多关于HarmonyOS鸿蒙Next中修饰对象数组的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


  1. @State只能观察第一层变化,无法深度观察嵌套对象
  2. 可以使用 @Observed + @ObjectLink 建立深度观察机制
  3. 也可以参考 数组对象的属性变化后无法触发UI的渲染刷新-行业常见问题-实用工具类行业实践
[@State](/user/State) items: Item[] = [
  UIUtils.makeV1Observed({ name: '张三', num: 18 }),
  UIUtils.makeV1Observed({ name: '李四', num: 20 })
]

问题原因

@State装饰的变量只能观察到第一层的变化。对于对象数组:

  • 可以观察到数组整体的赋值(如this.items = newItems
  • 可以观察到数组项的添加、删除、更新
  • 但无法观察到数组项对象内部属性的变化

解决方案

方案1:使用@Observed@ObjectLink(推荐)

[@Observed](/user/Observed)
class Item {
  name: string;
  
  constructor(name: string) {
    this.name = name;
  }
}

@Entry
@Component
struct Parent {
  [@State](/user/State) items: Item[] = [new Item('A'), new Item('B')];

  build() {
    Column() {
      ForEach(this.items, (item: Item, index: number) => {
        ChildComponent({ item: item })
      })
      Button('修改第一个项目')
        .onClick(() => {
          this.items[0].name = '修改后的值';
        })
    }
  }
}

@Component
struct ChildComponent {
  [@ObjectLink](/user/ObjectLink) item: Item;

  build() {
    Text(this.item.name)
  }
}

方案2:重新赋值整个数组

@Entry
@Component
struct Parent {
  [@State](/user/State) items: Item[] = [new Item('A'), new Item('B')];

  build() {
    Column() {
      ForEach(this.items, (item: Item, index: number) => {
        Text(item.name)
      })
      Button('修改第一个项目')
        .onClick(() => {
          // 创建新数组触发刷新
          this.items[0].name = '修改后的值';
          this.items = [...this.items]; // 重新赋值
        })
    }
  }
}

方案3:使用数组方法返回新数组

Button('修改项目')
  .onClick(() => {
    // 使用map返回新数组
    this.items = this.items.map((item, index) => {
      if (index === 0) {
        return new Item('修改后的值');
      }
      return item;
    });
  })

方案4:使用@Track装饰器

[@Observed](/user/Observed)
class Item {
  [@Track](/user/Track) name: string;
  
  constructor(name: string) {
    this.name = name;
  }
}

【问题背景】

  • “使用@State装饰器处理对象数组时,数据变化后页面无法同步刷新” 的问题

【解答思路】

  • 具体场景与技术局限可拆解为以下三点:
    1. @State装饰器的观察范围局限@State作为基础状态管理装饰器,仅能捕捉 “数组本身的第一层结构变化”—— 比如数组整体重新赋值、通过push/splice增删数组项等操作,可正常触发 UI 刷新;但当数组内存储的是对象(即 “对象数组”),若仅修改对象的内部属性(如数组[0].属性名 = 新值),@State无法感知这类深层变化,导致页面不更新。
    2. ForEach 组件的渲染逻辑关联:开发中通常用ForEach循环渲染对象数组数据,其渲染规则是 “基于键值匹配判断是否重建组件”—— 非首次渲染时,若键值未变则复用旧组件;若@State未感知到深层数据变化,ForEach无法触发组件重渲染,进一步导致页面无响应。
    3. 深层数据观察的需求场景:实际开发中,对象数组、嵌套对象(如 “对象内包含子对象”)等深层数据结构十分常见,这类结构的属性变化需要专门的状态观察机制,仅靠@State无法满足需求,需借助 HarmonyOS 提供的进阶装饰器解决。

针对 “@State无法观察对象数组深层属性变化” 的问题,核心解决方案是通过@Observed@ObjectLink装饰器的组合,突破@State的观察范围局限,实现深层数据变化与 UI 刷新的联动,具体思路分三步骤:

  1. 定位问题根源,确定核心方案:明确问题本质是@State仅能观察第一层数据,因此需引入 “针对类属性的观察工具”——@Observed用于标记数据模型类,使其内部属性变化可被捕捉;@ObjectLink用于在子组件中接收@Observed修饰的类实例,与父组件的@State变量建立双向数据绑定,从而传递数据变化信号。
  2. 分步实现:从数据模型到组件联动
    • 第一步:修饰数据模型类。为存储对象数组的 “数据模板类”(如示例中的Person类)添加@Observed装饰器,让该类的所有属性(如nameage)变化具备可观察性,成为 “可被追踪的数据源”。
    • 第二步:创建子组件承载数据。单独定义非@Entry的子组件(如示例中的MyPerson),用@ObjectLink装饰器声明接收 “@Observed修饰类的实例” 的变量(如person: Person)—— 此步骤需注意:@ObjectLink不可直接用于@Entry装饰的根组件,必须通过子组件间接使用。
    • 第三步:父组件组织数据与渲染。在@Entry根组件中,用@State装饰对象数组(如persons: Person[]),通过ForEach循环将数组中的每个Person实例传递给子组件;当点击按钮修改数组项的内部属性(如this.persons[0].age = 66)时,@Observed会捕捉属性变化,@ObjectLink会将变化同步到子组件,最终触发 UI 刷新。
  3. 明确关键约束与逻辑闭环:核心逻辑是 “@Observed负责‘感知属性变化’,@ObjectLink负责‘传递变化信号’”,二者配合突破@State的观察局限;同时需牢记@ObjectLink的使用限制(不支持@Entry组件),确保组件层级设计符合规则,最终实现 “对象数组深层属性变化→UI 同步刷新” 的效果

【官方文档——状态管理V1】

在HarmonyOS Next中,修饰对象数组主要使用ArkTS语法。通过@State@Link@Provide等装饰器管理数组状态,支持响应式更新。例如:@State myArray: Array<CustomObject> = []可实现数组数据绑定。数组操作需遵循ArkTS类型安全规范,确保元素类型一致。

在HarmonyOS Next中,使用@State修饰对象数组时,如果数组元素是对象类型,直接修改对象属性不会触发页面刷新。这是因为@State监听的是数组引用变化,而非对象内部属性变化。

解决方案:

  1. 重新赋值整个数组(推荐):
// 修改对象属性后
this.items = [...this.items];
  1. 使用@Observed@ObjectLink
[@Observed](/user/Observed)
class Item {
  // 类定义
}

@Component
struct MyComponent {
  [@ObjectLink](/user/ObjectLink) items: Item[];
  // 组件逻辑
}
  1. 使用@Track装饰对象属性
class Item {
  [@Track](/user/Track) name: string = '';
}

最佳实践

  • 对于简单场景,使用重新赋值数组的方式
  • 对于复杂对象结构,建议结合使用[@Observed](/user/Observed)[@ObjectLink](/user/ObjectLink)
  • 确保对象属性使用[@Track](/user/Track)装饰器标记需要监听的属性

这样可以确保对象数组中的数据变化能够正确触发页面UI更新。

回到顶部