HarmonyOS鸿蒙Next中@Track装饰类属性可能无法刷新UI的问题

HarmonyOS鸿蒙Next中@Track装饰类属性可能无法刷新UI的问题

  1. 点击 切换为1 UI正常显示为1

2.点击 替换整个实例 UI无变化,理论上应该显示为空

设备名称:HUAWEI Mate 60
HarmonyOS版本 5.1.0
软件版本:5.1.0.150
API版本:5.0.5(17)
OpenHarmony版本:5.0.1

@Observed
export class TestModel {
  [@Track](/user/Track) name?: string
}
import { TestModel } from '../model/TestModel'
import { hilog } from '@kit.PerformanceAnalysisKit'

@Entry
@Component
struct Index {
  @State @Watch('onModelChange') model: TestModel = new TestModel()

  build() {
    Column() {
      Button("切换为1")
        .id('HelloWorld')
        .fontSize($r('app.float.page_text_font_size'))
        .fontWeight(FontWeight.Bold)
        .onClick(() => {
          this.model.name = "1"
        })

      Button("替换整个实例")
        .id('HelloWorld')
        .fontSize($r('app.float.page_text_font_size'))
        .fontWeight(FontWeight.Bold)
        .onClick(() => {
          this.model = new TestModel()
        })

      Text(this.model.name ?? "空")
        .id('HelloWorld')
        .fontSize($r('app.float.page_text_font_size'))
        .fontWeight(FontWeight.Bold)
    }
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')

  }

  onModelChange(changedPropertyName: string) {
    hilog.info(0x0000, 'testTag', '%{public}s', 'onModelChange');

  }
}

更多关于HarmonyOS鸿蒙Next中@Track装饰类属性可能无法刷新UI的问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

这个问题看似简单,其实有很多细节需要探究

问题现象:当class的属性被装饰为@Track时,再次赋值undefined时,组件树没有刷新

原因:针对@Observed追踪的Tack属性赋值时,不对undefined进行追踪,所以组件不会刷新

解决方案:去掉@Observed@Track即可

详细步骤:

export class TestModel {
  name?: string
}

这样就会达到预期

扩展:

根据我的分析,这个跟代理有关,当class被@Observed装饰后,new出的class对象,会被代理,例如字段会加上“_ob”前缀,至于其中的原理不得而知,或许是一个bug

如果使用V2版本则不会有这个问题,即使给name赋值undefined,界面会正常刷新,根据这个现象反推v1,说明大概率是一个bug

如果对你有帮助,望采纳!

更多关于HarmonyOS鸿蒙Next中@Track装饰类属性可能无法刷新UI的问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


TestModel@Observed装饰(标记为可观察类),name属性用@Track装饰(标记该属性变化需触发 UI 更新);Index组件中model@State装饰(组件内部状态)。

  • 点击 “切换为 1” 生效:直接修改model.name@Track标记的属性),@Observed类会感知到属性变化,@State检测到依赖更新,触发 UI 刷新,符合预期。
  • 点击 “替换整个实例” 不生效:将model赋值为新的TestModel实例(引用变化),但 UI 未更新,核心原因是:在部分 ArkUI 版本(尤其是 API 5.x)中,@State@Observed装饰的类实例的 “引用替换” 追踪存在局限性 —— 框架可能优先依赖@Track标记的属性变化,而忽略了整个实例的引用替换,导致未触发 UI 刷新。

@Observed用于装饰类,使其具有观察变化的能力,而@Track装饰类的属性,当这些属性变化时触发UI更新。但是,当整个实例被替换时,可能需要重新装饰新的实例,否则ArkUI可能无法检测到新实例的变化。

@Observed 装饰的类实例被替换时,仅当类内部 @Track 属性变化才会触发 UI 更新。

this.model = new TestModel() 替换了整个实例,但新实例的 name 属性从未被赋值过(初始值为 undefined),未被 @Track 标记的初始状态不会触发 UI 刷新。

有两种方式解决,

1.给@Track 一个初始化值为undefined

2.提供一个有参构造器,必须初始化@Track修饰的属性

@Observed
export class TestModel {
    [@Track](/user/Track) name?: string =undefined
}

or

@Observed
export class TestModel {
  [@Track](/user/Track) name?: string

  constructor(name?: string) {
    this.name = name;
  }


}

虽然能解决,但是还是想知道原因

开发者你好,请参考我的楼层回复。

@Track装饰器用于标记类属性,当属性值变化时驱动UI更新。在鸿蒙Next中,若@Track装饰的属性未触发UI刷新,通常与以下原因有关:

  1. 属性未在组件内使用:UI仅依赖组件内使用的状态。
  2. 嵌套对象变更:直接修改对象内部属性而非整个对象赋值,需使用新对象覆盖原值。
  3. 状态管理范围:确保@Track属性在@State@Observed等装饰器管理范围内。

需检查属性变更方式及UI绑定逻辑。

这是一个典型的关于ArkUI状态管理和装饰器理解的问题。问题不在于@Track本身,而在于对@State@Watch工作机制的理解。

在你的代码中,@State装饰的model变量存储的是对TestModel实例的引用。当你点击“替换整个实例”时,this.model = new TestModel()确实创建了一个新的对象并赋值给了@State变量。

关键点在于@Watch('onModelChange')的触发条件@Watch装饰器仅在被装饰的变量本身被赋值时才会触发回调。在你的情况下:

  1. “切换为1”:修改的是model.name这个@Track属性,model这个引用本身没有改变,所以不会触发onModelChange。UI能更新是因为@Track装饰的name属性变化被ArkUI的状态管理系统侦测到了,并驱动了对应的Text组件刷新。
  2. “替换整个实例”:model引用被重新赋值,这会触发onModelChange(你的日志应该会打印)。但是,新的TestModel实例的name属性为undefined。此时,Text(this.model.name ?? "空")中的this.model.name仍然是undefined,因此显示后备值“空”。UI从显示“1”变为显示“空”,这个变化是发生的。你可能因为UI从“1”变成“空”,视觉上觉得是“无变化”,但实际上它已经变了。

如果你觉得UI“无变化”,请确认:

  1. 检查hilog日志,确认onModelChange是否被触发。
  2. 检查替换实例后,UI上是否显示“空”字。如果显示“空”,则UI已正确刷新。

总结:代码行为符合预期。@Track负责类内部属性的响应式,@State负责其引用的响应式,@Watch监听@State变量自身的赋值操作。两者协同工作,没有无法刷新UI的问题。

回到顶部