HarmonyOS鸿蒙Next中@Observed/@ObjectLink对嵌套对象使用的疑问

HarmonyOS鸿蒙Next中@Observed/@ObjectLink对嵌套对象使用的疑问 现在这个代码是有问题的,我点(父 修改深层城市) 或者 (子 修改年龄+城市)按钮,只有子组件里的内容发生了变化,父组件的 Text(父:${this.user.name} | ${this.user.profile.age} | ${this.user.profile.addr.city})没有变化,研究了半天也没理解这样为什么错了

// 第三层
[@Observed](/user/Observed)
class Address {
  city: string;
  constructor(city: string) {
    this.city = city;
  }
}

// 第二层
[@Observed](/user/Observed)
class Profile {
  age: number;
  addr: Address; // 嵌套下层对象
  constructor(age: number, addr: Address) {
    this.age = age;
    this.addr = addr;
  }
}

// 最外层
[@Observed](/user/Observed)
class User {
  name: string;
  profile: Profile; // 嵌套中层对象
  constructor(name: string, profile: Profile) {
    this.name = name;
    this.profile = profile;
  }
}

@Entry
@Component
struct Parent {
  // 必须 @State 包裹 Observed 对象
  @State user: User = new User("张三",
    new Profile(20, new Address("北京"))
  );

  build() {
    Column({ space: 20 }) {
      Text(`父:${this.user.name} | ${this.user.profile.age} | ${this.user.profile.addr.city}`)

      // 把 嵌套的成员对象 传给子
      Child({
        profile: this.user.profile,
        addr: this.user.profile.addr
      })

      // 修改深层属性(不改变引用地址,只改内部值)
      Button("父 修改深层城市")
        .onClick(() => {
          this.user.profile.addr.city = "上海";
          console.log('11',JSON.stringify(this.user))
        })
    }
    .padding(20)
  }
}

@Component
struct Child {
  // 接收第二层对象
  [@ObjectLink](/user/ObjectLink) profile: Profile;
  // 接收第三层嵌套对象
  [@ObjectLink](/user/ObjectLink) addr: Address;

  build() {
    Column({ space: 10 }) {
      Text(`子年龄:${this.profile.age}`)
      Text(`子城市:${this.addr.city}`)

      Button("子 修改年龄+城市")
        .onClick(() => {
          this.profile.age = 99;
          this.addr.city = "广州";
        })
    }
    .border({width:1})
    .padding(10)
  }
}

更多关于HarmonyOS鸿蒙Next中@Observed/@ObjectLink对嵌套对象使用的疑问的实战教程也可以访问 https://www.itying.com/category-93-b0.html

6 回复

对于@Entry装饰的Parent组件内状态变量@State user: User,只有user.name是第一层。因此,当点击 Button(“父 修改深层城市”)直接修改 this.user.profile.addr.city = “上海”;时,Index中的Text(父:${this.user.name} | ${this.user.profile.age} | ${this.user.profile.addr.city})不会刷新,因为@State只能观察到第一层属性变化,不能直接观察嵌套对象内部属性的变化。所以如果 Button(“父 修改深层城市”)直接修改 this.user.name = "李四"时,是可以观察到并正常更新的。

如果希望都更新,可以做如下修改:

// 第三层
@Observed
class Address {
  city: string;
  constructor(city: string) {
    this.city = city;
  }
}

// 第二层
@Observed
class Profile {
  age: number;
  addr: Address; // 嵌套下层对象
  constructor(age: number, addr: Address) {
    this.age = age;
    this.addr = addr;
  }
}

// 最外层
@Observed
class User {
  name: string;
  profile: Profile; // 嵌套中层对象
  constructor(name: string, profile: Profile) {
    this.name = name;
    this.profile = profile;
  }
}

[@Entry](/user/Entry)
@Component
struct Parent {
  // 必须 [@State](/user/State) 包裹 Observed 对象
  [@State](/user/State) user: User = new User("张三",
    new Profile(20, new Address("北京"))
  );

  build() {
    Column({ space: 20 }) {
      Text(`父:${this.user.name} | ${this.user.profile.age} | ${this.user.profile.addr.city}`)

      // 把 嵌套的成员对象 传给子
      Child({
        user:this.user
      })

      // 修改深层属性(不改变引用地址,只改内部值)
      Button("父 修改深层城市")
        .onClick(() => {
          this.user.profile.addr.city = "上海";
          this.user.name='李四'
          console.log('11',JSON.stringify(this.user))
        })
    }
    .padding(20)
  }
}

@Component
struct Child {
  // 接收第二层对象
  //@ObjectLink profile: Profile;
  // 接收第三层嵌套对象
  //@ObjectLink addr: Address;
  @ObjectLink user:User


  build() {
    Column({ space: 10 }) {
      Text(`子年龄:${this.user.profile.age}`)
      Text(`子城市:${this.user.profile.addr.city}`)

      Button("子 修改年龄+城市")
        .onClick(() => {
          this.user.profile.age = 99;
          this.user.profile.addr.city = "广州";
          this.user.name='wangwu'
        })
    }
    .border({width:1})
    .padding(10)
  }
}

更多关于HarmonyOS鸿蒙Next中@Observed/@ObjectLink对嵌套对象使用的疑问的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


学习了

十分感谢,我明白了,我在父组件里state修饰的是user,所以我传对象给子元素的时候也必须传被state修饰的是user

为什么不变化:@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化-管理组件拥有的状态-状态管理(V1)

解决方案采用V2:

// 第三层
@ObservedV2
class Address {
        @Trace city: string;

        constructor(city: string) {
                this.city = city;
        }
}

// 第二层
@ObservedV2
class Profile {
        @Trace age: number;
        @Trace addr: Address; // 嵌套下层对象

        constructor(age: number, addr: Address) {
                this.age = age;
                this.addr = addr;
        }
}

// 最外层
@ObservedV2
class User {
        @Trace name: string;
        @Trace profile: Profile; // 嵌套中层对象

        constructor(name: string, profile: Profile) {
                this.name = name;
                this.profile = profile;
        }
}

@Entry
@ComponentV2
struct Parent {
        // 必须 @State 包裹 Observed 对象
        @Local user: User = new User("张三",
                new Profile(20, new Address("北京"))
        );

        build() {
                Column({ space: 20 }) {
                        Text(`父:${this.user.name} | ${this.user.profile.age} | ${this.user.profile.addr.city}`)

                        // 把 嵌套的成员对象 传给子
                        Child({
                                profile: this.user.profile,
                                addr: this.user.profile.addr
                        })

                        // 修改深层属性(不改变引用地址,只改内部值)
                        Button("父 修改深层城市")
                                .onClick(() => {
                                        this.user.profile.addr.city = "上海";
                                        console.log('11', JSON.stringify(this.user))
                                })
                }
                .padding(20)
        }
}

@ComponentV2
struct Child {
        // 接收第二层对象
        @Param @Require profile: Profile;
        // 接收第三层嵌套对象
        @Param @Require addr: Address;

        build() {
                Column({ space: 10 }) {
                        Text(`子年龄:${this.profile.age}`)
                        Text(`子城市:${this.addr.city}`)

                        Button("子 修改年龄+城市")
                                .onClick(() => {
                                        this.profile.age = 99;
                                        this.addr.city = "广州";
                                })
                }
                .border({ width: 1 })
                .padding(10)
        }
}

在HarmonyOS鸿蒙Next的ArkTS中,**@Observed用于装饰类,使其实例可被观察;@ObjectLink**用于父组件中引用被@Observed装饰的子对象,实现嵌套数据的双向同步。嵌套对象需每层均用@Observed装饰,数组内对象同样需@Observed,否则无法触发更新。@ObjectLink只能配合@Observed使用,不可用于普通对象或简单类型。

在 HarmonyOS Next 中,父组件 @State user 只能观测到 user 本身的第一层属性变化(例如 user.nameuser 被整体替换),无法自动追踪深层嵌套对象的内部属性修改
当执行 this.user.profile.addr.city = "上海" 时,user.profile 引用未变、addr 引用未变,仅 Address 的内部 city 变了,@State 感知不到这一变化,因此父组件的 Text 不会刷新。

而子组件通过 @ObjectLink 接收 ProfileAddress 对象,这两个类均为 @Observed@ObjectLink 会订阅该实例的属性变更,所以子组件正常刷新。

这是状态观察的深度限制@State 仅浅层观察,@ObjectLink 直接绑定被观测实例可响应其任意深度属性修改。若需父组件同步更新,必须让父组件能感知到引用变化(例如替换 profile 对象)。

回到顶部