HarmonyOS鸿蒙Next中ArkTS的@Observed和@ObjectLink在深层嵌套对象更新时,会不会导致不必要的父组件重渲染?

HarmonyOS鸿蒙Next中ArkTS的@Observed@ObjectLink在深层嵌套对象更新时,会不会导致不必要的父组件重渲染? 我的数据结构是 { user: { profile: { avatar: string } } },只改 avatar,但整个用户卡片都刷新了。如何精准更新?

6 回复

 @Observed在父组件用,@ObjectLink在子组件用。avatar改变了,应该只有子组件更新了。父组件不会更新的。你是这样用的么

更多关于HarmonyOS鸿蒙Next中ArkTS的@Observed和@ObjectLink在深层嵌套对象更新时,会不会导致不必要的父组件重渲染?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


使用@Track装饰器可以避免冗余刷新。

@Track应用于class对象的属性级更新。@Track装饰的属性变化时,只会触发该属性关联的UI更新。

限制:如果class类中使用了@Track装饰器,则未被@Track装饰器装饰的属性不能在UI中使用,如果使用,会发生运行时报错。

import { hilog } from '@kit.PerformanceAnalysisKit';
const DOMAIN_NUMBER: number = 0XFF00;
const TAG: string = '[Sample_StateTrack]';

class LogTrack {
  [@Track](/user/Track) public str1: string;
  [@Track](/user/Track) public str2: string;

  constructor(str1: string) {
    this.str1 = str1;
    this.str2 = 'World';
  }
}

class LogNotTrack {
  public str1: string;
  public str2: string;

  constructor(str1: string) {
    this.str1 = str1;
    this.str2 = 'World';
  }
}

@Entry
@Component
struct AddLog {
  @State logTrack: LogTrack = new LogTrack('Hello');
  @State logNotTrack: LogNotTrack = new LogNotTrack('Hello');

  isRender(index: number) {
    hilog.info(DOMAIN_NUMBER, TAG, `Text ${index} is rendered`);
    return 50;
  }

  build() {
    Row() {
      Column() {
        Text(this.logTrack.str1) // Text1
          .id('str1')
          .fontSize(this.isRender(1))
          .fontWeight(FontWeight.Bold)
        Text(this.logTrack.str2) // Text2
          .fontSize(this.isRender(2))
          .fontWeight(FontWeight.Bold)
        Button('change logTrack.str1')
          .id('str2')
          .onClick(() => {
            this.logTrack.str1 = 'Bye';
          })
        Text(this.logNotTrack.str1) // Text3
          .fontSize(this.isRender(3))
          .fontWeight(FontWeight.Bold)
        Text(this.logNotTrack.str2) // Text4
          .fontSize(this.isRender(4))
          .fontWeight(FontWeight.Bold)
        Button('change logNotTrack.str1')
          .onClick(() => {
            this.logNotTrack.str1 = 'Bye';
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

在上面的示例中:

  1. 类LogTrack中的属性均被@Track装饰器装饰,点击按钮"change logTrack.str1",此时Text1刷新,Text2不刷新,只有一条日志输出,避免了冗余刷新
  2. 类logNotTrack中的属性均未被@Track装饰器装饰,点击按钮"change logNotTrack.str1",此时Text3、Text4均会刷新,有两条日志输出,存在冗余刷新。

本身就会刷新吧,而且不会有啥太大影响

没碰到过

在HarmonyOS鸿蒙Next中,ArkTS的@Observed@ObjectLink用于深层嵌套对象更新时,不会直接导致不必要的父组件重渲染。@ObjectLink装饰器会创建对@Observed装饰的对象的内部属性的双向数据绑定。当被观察的嵌套对象的属性发生变化时,只有使用了该特定@ObjectLink变量的组件会重新渲染,而父组件不会因此触发不必要的重渲染。

在HarmonyOS Next的ArkUI中,@Observed@ObjectLink的设计初衷是实现对嵌套对象属性的精细观察和响应。根据你的描述,当只修改深层属性user.profile.avatar时,整个用户卡片发生重渲染,这通常意味着数据观察的粒度设置不够精确。

核心问题在于:@Observed装饰的类,其所有属性都被视为可观察的。在你的嵌套结构{ user: { profile: { avatar: string } } }中,如果userprofile本身被@Observed装饰,那么对avatar的修改会触发从userprofile开始的属性变更通知,从而导致依赖它们的组件(整个用户卡片)进行更新检查。

要实现精准更新,关键在于让观察点尽可能接近实际发生变化的属性。建议方案如下:

  1. 精细化装饰:确保只有真正需要被观察的类才使用@Observed。对于你的数据结构:

    • profile类用@Observed装饰。
    • user类中,将profile成员变量用@ObjectLink装饰(或使用@Observed装饰user类,但profile@ObjectLink建立关联)。
    • 在组件中,只@ObjectLink链接到具体的profile对象,而不是整个user对象。
  2. 代码结构示例

    @Observed
    class Profile {
      avatar: string = '';
      // ... 其他属性
    }
    
    class User { // User类可以不用@Observed
      profile: Profile = new Profile(); // 或者用@ObjectLink装饰,如果User也是@Observed
    }
    
    @Component
    struct UserCard {
      @ObjectLink profile: Profile; // 只观察profile
    
      build() {
        // 组件只使用this.profile.avatar
        // 当avatar更新时,只有依赖此@ObjectLink profile的组件会重新渲染
      }
    }
    
  3. 使用@Track装饰器:如果希望在一个@Observed类内部,只对某些特定属性的修改触发UI更新,可以使用@Track装饰该属性。这样,即使类被@Observed装饰,也只有被@Track装饰的属性变化会通知UI更新。但这更适用于类内部多个独立可观察属性的场景。

总结:要避免不必要的父组件重渲染,应确保@ObjectLink指向的是最内层、变化最频繁的@Observed对象(在你的案例中是profile),而不是外层的大对象(user)。通过将观察点下沉,可以最大限度地缩小UI更新的范围。

回到顶部