【高心星出品】
V2 装饰器 @ObservedV2 和 @Trace 的使用
概念
@ObservedV2 装饰器与 @Trace 装饰器是 HarmonyOS 状态管理 V2 中用于深度观测类属性变化的核心工具。它们主要解决嵌套类对象属性变化的观测难题,以下是关键特性和使用要点:
@ObservedV2 装饰器与 @Trace 装饰器需要配合使用,单独使用 @ObservedV2 装饰器或 @Trace 装饰器没有任何作用。
被 @Trace 装饰器装饰的属性 property 变化时,仅会通知 property 关联的组件进行刷新。
在嵌套类中,嵌套类中的属性 property 被 @Trace 装饰且嵌套类被 @ObservedV2 装饰时,才具有触发 UI 刷新的能力。
在继承类中,父类或子类中的属性 property 被 @Trace 装饰且该 property 所在类被 @ObservedV2 装饰时,才具有触发 UI 刷新的能力。
未被 @Trace 装饰的属性用在 UI 中无法感知到变化,也无法触发 UI 刷新。
@ObservedV2 的类实例目前不支持使用 JSON.stringify 进行序列化。
使用 @ObservedV2 与 @Trace 装饰器的类,需通过 new 操作符实例化后,才具备被观测变化的能力。
一、核心机制
协同工作原则:
必须同时使用 @ObservedV2(类级装饰器)和 @Trace(属性级装饰器)
单独使用任一装饰器均无效
示例结构:
@ObservedV2
class User {
@Trace name: string;
@Trace address: Address;
}
复制代码
深度观测能力:
支持嵌套类结构(如类 A 包含类 B 实例,B 又包含类 C 实例)
支持继承关系(父类/子类属性变化均能被观测)
仅被 @Trace 装饰的属性变化触发 UI 刷新
二、与 V1 版本的对比
三、关键限制条件
序列化限制:被 @ObservedV2 装饰的类实例不支持 JSON.stringify
实例化要求:必须通过new
操作符创建实例
兼容性限制:
禁止与 V1 装饰器(如 @State、@Observed)混用
继承自 @ObservedV2 的类不可与 V1 装饰器共用
数组处理:仅支持基础类型数组的标准 API 操作(如 push/splice)
案例
没有使用 @observedv2 和 @trace 装饰器的时候,如果想更新 UI,需要 @local 配合新创建对象。
下面案例两个按钮点击的时候重新 new 了 student 对象,才引起了 UI 刷新,如果单独修改属性则不会引起刷新。
class Student{
name:string
age:number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
@Entry
@ComponentV2
struct Observerpage {
// @local装饰只会观察到对象引用的变化
@Local student:Student=new Student('gxx',20)
build() {
Column({space:20}){
Button('姓名: '+this.student.name)
.width('60%')
.onClick(()=>{
// 更新对象可以引起UI刷新
this.student=new Student('ggl',30)
// 不会引起UI刷新,@local观察不到属性的变化
this.student.name='ggl'
})
Button('年龄: '+this.student.age)
.width('60%')
.onClick(()=>{
// 更新对象可以引起UI刷新
this.student=new Student('xyz',33)
})
}
.height('100%')
.width('100%')
}
}
复制代码
有使用 @observedv2 和 @trace 装饰器的时候,如果想更新 UI,不需要 @local 装饰器,可以直接更新属性即可。
下面案例中 name 被 @Trace 装饰,age 没有被 @Trace 装饰,student 没有被 @local 装饰,所以更新对象的时候不会引起刷新,name 更新的时候会刷新,age 更新则不会刷新。
@ObservedV2
class Student{
@Trace name:string
age:number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
@Entry
@ComponentV2
struct Observerpage1 {
// 不需要@local 都可以观察到属性的变化 但是无法观察对象的变化
student:Student=new Student('gxx',20)
build() {
Column({space:20}){
Button('姓名: '+this.student.name)
.width('60%')
.onClick(()=>{
// 更新对象无法可以引起UI刷新 因为没有@local装饰
// this.student=new Student('ggl',30)
// 会引起UI刷新,属性被@Trace装饰
this.student.name='ggl'
})
Button('年龄: '+this.student.age)
.width('60%')
.onClick(()=>{
// 不会引起UI刷新 没有被@Trace装饰
this.student.age+=10
})
}
.height('100%')
.width('100%')
}
}
复制代码
嵌套类的情况:
下面案例里面 user 嵌套了 address,我们需要给两个类都加 @observedv2 装饰器,user 里面的 name 需要加 @trace 而 address 嵌套类里面已经有 @trace 装饰,则本身不用 @trace 装饰了。
@ObservedV2
class Address {
@Trace city: string;
constructor(city: string) {
this.city = city;
}
}
@ObservedV2
class User {
@Trace name: string;
address: Address; //这里即使不加@Trace也会被观察到 嵌套类属性变化可触发UI刷新
constructor(name: string, address: Address) {
this.name = name;
this.address = address;
}
}
@Entry
@ComponentV2
struct Observerpage2 {
// 不需要@local 都可以观察到属性的变化 但是无法观察对象的变化
addr:Address=new Address('商丘')
u:User=new User('gxx',this.addr)
build() {
Column({space:20}){
Button('姓名: '+this.u.name)
.width('60%')
.onClick(()=>{
// 会引起UI刷新,属性被@Trace装饰
this.u.name='ggl'
})
Button('地址: '+this.u.address.city)
.width('60%')
.onClick(()=>{
// 会引起UI刷新
this.u.address.city='郑州'
})
}
.height('100%')
.width('100%')
}
}
复制代码
类继承的情况:
下面案例里面 dog 类继承了 animal,dog 类里面的属性就有了 animal 中的属性,并且都由 @Trace 装饰,所以属性更新会引起 UI 更新。
@ObservedV2
class Animal {
@Trace age: number;
constructor(age: number) {
this.age = age;
}
}
@ObservedV2
class Dog extends Animal {
@Trace breed: string; // 继承类属性变化可触发UI刷新
constructor(age: number, breed: string) {
super(age);
this.breed = breed;
}
}
@Entry
@ComponentV2
struct Observerpage3 {
// 不需要@local 都可以观察到属性的变化 但是无法观察对象的变化
dog:Dog=new Dog(18,'哈士奇')
build() {
Column({space:20}){
Button('年龄: '+this.dog.age)
.width('60%')
.onClick(()=>{
// 会引起UI刷新,继承过来的
this.dog.age=10
})
Button('品种: '+this.dog.breed)
.width('60%')
.onClick(()=>{
// 会引起UI刷新 属性被@Trace装饰
this.dog.breed='藏獒'
})
}
.height('100%')
.width('100%')
}
}
复制代码
评论