基于状态变量实现复杂对象的状态监听

海不辞水
发布于 2024-7-30 11:53
浏览
0收藏

场景一:简单对象监听

对象在我们开发过程中是很常见的数据类型,我们在进行UI渲染的时候经常会用到对象,这里提供简单对象(所有字段均为基本数据类型)的监听效果。

方案一:状态管理V1实现

简单对象可以直接使用@State观测,这里使用起来比较简单,但是需要注意的是如果某个Class在页面中渲染中使用的字段较多的时候,可以结合@Track修饰器来进行观测。

实现代码:

@Builder 
export function SimpleObjectBuilder(name: string, param: Object) { 
  SimpleObject() 
} 
 
class LogTrack { 
  // 这里使用@Track可以达到最小化更新的效果 
  @Track str1: string; 
  @Track str2: string; 
 
  constructor(str1: string) { 
    this.str1 = str1; 
    this.str2 = 'World'; 
  } 
} 
 
class LogNotTrack { 
  str1: string; 
  str2: string; 
 
  constructor(str1: string) { 
    this.str1 = str1; 
    this.str2 = '世界'; 
  } 
} 
 
@Component 
export struct SimpleObject { 
  pathStack: NavPathStack = new NavPathStack() 
  @State logTrack: LogTrack = new LogTrack('Hello'); 
  @State logNotTrack: LogNotTrack = new LogNotTrack('你好'); 
 
  isRender(index: number) { 
    console.log(`Text ${index} is rendered`); 
    return 50; 
  } 
 
  build() { 
    NavDestination() { 
      Column() { 
        Column() { 
          Text(this.logTrack.str1)// UINode1 
            .fontSize(this.isRender(1)) 
            .fontWeight(FontWeight.Bold) 
          Text(this.logTrack.str2)// UINode2 
            .fontSize(this.isRender(2)) 
            .fontWeight(FontWeight.Bold) 
          Button('change logTrack.str1') 
            .onClick(() => { 
              // 点击更新str1 字段,观察页面日志变化,会发现使用@Track修饰的变量只会对改变的变量做最小化更新 
              this.logTrack.str1 = 'Bye'; 
            }) 
          Text(this.logNotTrack.str1)// UINode3 
            .fontSize(this.isRender(3)) 
            .fontWeight(FontWeight.Bold) 
          Text(this.logNotTrack.str2)// UINode4 
            .fontSize(this.isRender(4)) 
            .fontWeight(FontWeight.Bold) 
          Button('change logNotTrack.str1') 
            .onClick(() => { 
              // 点击更新str1字段,观察页面日志变化,会发现在不使用@Track修饰即使只改变了一个值,但是对象中所有的字段全都会触发UI更新那么值没有发生改变 
              this.logNotTrack.str1 = '再见'; 
            }) 
        } 
      }.width('100%').height('100%') 
    }.onReady((context: NavDestinationContext) => { 
      this.pathStack = context.pathStack; 
    }) 
  } 
}

实现效果如下:

基于状态变量实现复杂对象的状态监听-鸿蒙开发者社区

观察日志发现第一种在更新UI的时候只会对操作的UI进行重绘渲染,而第二种在更新UI的时候所有在页面中用到了的组件都会进行重绘,所以为了解释性能,我们更建议在Class中使用@Track对字段进行精准修饰。

方案二:状态管理V2实现

在状态管理V2中对于对象的观察只有一套框架@ObservedV2+@Trace,它们具备深度观测对象的能力,简单对象也同样可以观测。

需要注意的是使用@ObservedV2修饰的类,如果字段需要观测变化的时候需要对该字段使用@Trace进行修饰,否则UI不会刷新。

实现代码:

@Builder 
export function SimpleObjectBuilderV2(name: string, param: Object) { 
  SimpleObject() 
} 
 
@ObservedV2 
class LogTrack { 
  @Trace str1: string; 
  str2: string; 
 
  constructor(str1: string) { 
    this.str1 = str1; 
    this.str2 = 'World'; 
  } 
} 
 
@ComponentV2 
struct SimpleObject { 
  pathStack: NavPathStack = new NavPathStack() 
  logTrack: LogTrack = new LogTrack('hello'); 
 
  isRender(index: number) { 
    console.log(`Text ${index} is rendered`); 
    return 50; 
  } 
 
  build() { 
    NavDestination() { 
      Column() { 
        Text(this.logTrack.str1)// UINode1 
          .fontSize(this.isRender(1)) 
          .fontWeight(FontWeight.Bold) 
        Text(this.logTrack.str2)// UINode2 
          .fontSize(this.isRender(2)) 
          .fontWeight(FontWeight.Bold) 
        Button('change logTrack.str1') 
          .onClick(() => { 
            // 点击更新Str1和str2,str1使用了@Trace修饰,UI界面正常刷新,Str2没有修饰,UI界面不刷新 
            this.logTrack.str1 = 'Hi'; 
            this.logTrack.str2 = '世界'; 
          }) 
      } 
    }.onReady((context: NavDestinationContext) => { 
      this.pathStack = context.pathStack; 
    }) 
  } 
}

基于状态变量实现复杂对象的状态监听-鸿蒙开发者社区

并且使用@Trace修饰的字段也具备最小化更新的能力,具体见下方日志:

基于状态变量实现复杂对象的状态监听-鸿蒙开发者社区

另外还需要注意的是,被@ObservedV2与@Trace装饰的类对象实例,虽然具有深度观测对象属性的能力。但当对对象整体赋值时,UI却无法刷新。使用@Local装饰对象,可以达到观测对象本身变化的效果。

实现代码:

@ObservedV2 
class Info { 
  name: string; 
  age: number; 
  constructor(name: string, age: number) { 
    this.name = name; 
    this.age = age; 
  } 
} 
@Entry 
@ComponentV2 
struct Index { 
  info1: Info = new Info('Tom', 25); 
  @Local info2: Info = new Info('Tom', 25); 
  build() { 
    Column() { 
      Text(`info1: ${this.info1.name}-${this.info1.age}`) // Text1 
      Text(`info2: ${this.info2.name}-${this.info2.age}`) // Text2 
      Button('change info1&info2') 
        .onClick(() => { 
          this.info1 = new Info('Lucy', 18); // Text1不会刷新 
          this.info2 = new Info('Lucy', 18); // Text2会刷新 
        }) 
    } 
  } 
}

场景二:复杂对象监听

复杂对象一般是指在对象字段中还存在其他对象(嵌套对象)的场景。

方案一:状态管理V1实现

@State装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套对象的情况,它们的第二层的属性变化是无法观察到的。我们先看数据模型类图:

基于状态变量实现复杂对象的状态监听-鸿蒙开发者社区

在上面数据模型中,Class User的字段bag是Class Bag类型,Class Book的字段bookName是Class BookName类型,这种情况都是一个对象的字段中还含有一个对象,这一类数据我们叫复杂对象,这类数据在使用状态变量监听的时候会比较麻烦,@State/@Prop等修饰器只能监听一层,也就是对象下的基本数据类型字段,对于多次嵌套的对象,在深度监听的时候需要用@Observed/@ObjectLink装饰器来进行深度观测,请看下面实现;总结下面代码,我们不难发现在状态管理V1中一层组件永远只能监听一层对象,如果涉及到多层对象,我们就需要分子组件来进行监听同步。

@Builder 
export function ComplexObjectBuilderV1(name: string, param: Object) { 
  ComplexObject() 
} 
 
// objectLinkNestedObjects.ets 
let NextID: number = 1; 
 
// 这里的对象我们都需要深度监听,Class类先使用@Observed修饰 
@Observed 
class Bag { 
  public id: number; 
  public size: number; 
 
  constructor(size: number) { 
    this.id = NextID++; 
    this.size = size; 
  } 
} 
 
@Observed 
class User { 
  public bag: Bag; 
 
  constructor(bag: Bag) { 
    this.bag = bag; 
  } 
} 
 
@Observed 
class Book { 
  public bookName: BookName; 
 
  constructor(bookName: BookName) { 
    this.bookName = bookName; 
  } 
} 
 
@Observed 
class BookName extends Bag { 
  public nameSize: number; 
 
  constructor(nameSize: number) { 
    // 调用父类方法对nameSize进行处理 
    super(nameSize); 
    this.nameSize = nameSize; 
  } 
} 
 
@Component 
export struct ComplexObject { 
  pathStack: NavPathStack = new NavPathStack() 
  // 初始化状态变量,因为@ObjectLink修饰器的入参类型必须也是状态变量,所以我们第一层使用@State修饰 
  @State user: User = new User(new Bag(0)); 
  @State child: Book = new Book(new BookName(0)); 
 
  build() { 
    NavDestination() { 
      Column() { 
        // @ObjectLink是子组件与父组件双向同步,对于这类复杂对象无法做到在一个组件中实现深度监听,需要分层级来实现,嵌套多少层就需要分多少层组件 
        ViewA({ label: 'ViewA #1', bag: this.user.bag }) 
          .width(320) 
        ViewC({ label: 'ViewC #3', bookName: this.child.bookName }) 
          .width(320) 
        Button(`ViewC: this.child.bookName.size add 10`) 
          .width(320) 
          .backgroundColor('#ff17a98d') 
          .margin(10) 
          .onClick(() => { 
            // 使用@State修饰并且子组件使用了@ObjectLink修饰,因此能达到父组件状态子组件同步的效果,在父组件更新可以生效,如果子组件没有使用@ObjectLink修饰,则不生效 
            this.child.bookName.size += 10 
            console.log('this.child.bookName.size:' + this.child.bookName.size) 
          }) 
        Button(`ViewB: this.user.bag = new Bag(10)`) 
          .width(320) 
          .backgroundColor('#ff17a98d') 
          .margin(10) 
          .onClick(() => { 
            // 直接替换user.bag,此时相当于是bag是整个值替换,因为@State能够监听一层,所以UI会改变,这里的原理与简单对象监听原理一致 
            this.user.bag = new Bag(10); 
          }) 
        Button(`ViewB: this.user = new User(new Bag(20))`) 
          .width(320) 
          .backgroundColor('#ff17a98d') 
          .margin(10) 
          .onClick(() => { 
            // 直接替换整个user使用@State能够监听到user的变化,这里就相当于基本数据类型使用@State监听原理一致 
            this.user = new User(new Bag(20)); 
          }) 
 
      }.width('100%').height('100%') 
    }.onReady((context: NavDestinationContext) => { 
      this.pathStack = context.pathStack; 
    }) 
  } 
} 
 
@Component 
struct ViewA { 
  label: string = 'ViewA'; 
  // 接收父级对象的Bag对象 
  @ObjectLink bag: Bag; 
 
  build() { 
    Column() { 
      Text(`ViewC [${this.label}] this.bag.size = ${this.bag.size}`) 
        .fontColor('#ffffffff') 
        .backgroundColor('#ff3d9dba') 
        .width(320) 
        .height(50) 
        .margin(10) 
        .textAlign(TextAlign.Center) 
      Button(`ViewA: this.bag.size add 1`) 
        .width(320) 
        .backgroundColor('#ff17a98d') 
        .margin(10) 
        .onClick(() => { 
          // 更新子组件bag的字段,因为使用了@ObjeckLink修饰父组件传入的对象,可以达到父子组件同步的效果,因此UI会刷新 
          this.bag.size += 1; 
        }) 
    } 
  } 
} 
 
@Component 
struct ViewC { 
  label: string = 'ViewC1'; 
  @ObjectLink bookName: BookName; 
 
  build() { 
    Row() { 
      Column() { 
        Text(`ViewC [${this.label}] this.bookName.size = ${this.bookName.size}`) 
          .fontColor('#ffffffff') 
          .backgroundColor('#ff3d9dba') 
          .width(320) 
          .height(50) 
          .margin(10) 
          .textAlign(TextAlign.Center) 
        Button(`ViewC: this.bookName.size add 1`) 
          .width(320) 
          .backgroundColor('#ff17a98d') 
          .margin(10) 
          .onClick(() => { 
            // 与上面同理,因为使用了@ObjectLink修饰,能达到父子组件同步的效果,因此子组件状态变化UI会刷新 
            this.bookName.size += 1; 
            console.log('this.bookName.size:' + this.bookName.size) 
          }) 
      } 
      .width(320) 
    } 
  } 
}

实现效果图如下:

基于状态变量实现复杂对象的状态监听-鸿蒙开发者社区

方案二:状态管理V2实现

同样场景下,类图见状态管理V1,在状态管理V2中实现起来就要方便的多,我们在场景一中介绍了,状态管理V2对于对象的监听只有@ObservedV2+@Trace修饰器,复杂对象我们也是使用这个修饰器,具体实现见下面代码。

在使用状态管理V2进行观测的时候,我们无须考虑当前对象是否是多层嵌套对象,需要需要分子组件来进行观测,只需要关注Class中的字段是否需要展示在UI中即可。

@Builder 
export function ComplexObjectBuilderV2(name: string, param: Object) { 
  ComplexObject() 
} 
 
let NextID: number = 1; 
 
@ObservedV2 
class Bag { 
  public id: number; 
  // 在UI中展示的字段需要使用@Trace修饰,否则无法观测,UI无法刷线 
  @Trace public size: number; 
 
  constructor(size: number) { 
    this.id = NextID++; 
    this.size = size; 
  } 
} 
 
@ObservedV2 
class User { 
  @Trace public bag: Bag; 
 
  constructor(bag: Bag) { 
    this.bag = bag; 
  } 
} 
 
@ObservedV2 
class Book { 
  @Trace public bookName: BookName; 
 
  constructor(bookName: BookName) { 
    this.bookName = bookName; 
  } 
} 
 
@ObservedV2 
class BookName extends Bag { 
  public nameSize: number; 
 
  constructor(nameSize: number) { 
    // 调用父类方法对nameSize进行处理 
    super(nameSize); 
    this.nameSize = nameSize; 
  } 
} 
 
@ComponentV2 
export struct ComplexObject { 
  pathStack: NavPathStack = new NavPathStack() 
  // 初始化状态变量,在简单对象修饰中讲解过了,@ObservedV2+@Trace能实现对象的深度观测,但是当自身被替换的时候无法观测到,我们需要使用@Local观测 
  @Local user: User = new User(new Bag(0)); 
  @Local child: Book = new Book(new BookName(0)); 
 
  build() { 
    NavDestination() { 
      Column() { 
        Column() { 
          Text(`this.user.bag.size = ${this.user.bag.size}`) 
            .fontColor('#ffffffff') 
            .backgroundColor('#ff3d9dba') 
            .width(320) 
            .height(50) 
            .margin(10) 
            .textAlign(TextAlign.Center) 
          Button(`ViewA: this.bag.size add 1`) 
            .width(320) 
            .backgroundColor('#ff17a98d') 
            .margin(10) 
            .onClick(() => { 
              // @Trace修饰的字段具备深度观测能力,可以直接修改 
              this.user.bag.size += 1; 
            }) 
        }.width(320) 
 
        Column() { 
          Text(`this.child.bookName.size = ${this.child.bookName.size}`) 
            .fontColor('#ffffffff') 
            .backgroundColor('#ff3d9dba') 
            .width(320) 
            .height(50) 
            .margin(10) 
            .textAlign(TextAlign.Center) 
          Button(`ViewC: this.bookName.size add 1`) 
            .width(320) 
            .backgroundColor('#ff17a98d') 
            .margin(10) 
            .onClick(() => { 
              // @Trace修饰的字段具备深度观测能力,可以直接修改 
              this.child.bookName.size += 1; 
              console.log('this.bookName.size:' + this.child.bookName.size) 
            }) 
        }.width(320) 
 
        Button(`ViewC: this.child.bookName.size add 10`) 
          .width(320) 
          .backgroundColor('#ff17a98d') 
          .margin(10) 
          .onClick(() => { 
            // @Trace修饰的字段具备深度观测能力,可以直接修改 
            this.child.bookName.size += 10 
            console.log('this.child.bookName.size:' + this.child.bookName.size) 
          }) 
        Button(`ViewB: this.user.bag = new Bag(10)`) 
          .width(320) 
          .backgroundColor('#ff17a98d') 
          .margin(10) 
          .onClick(() => { 
            // @Trace修饰的字段具备深度观测能力,可以直接修改 
            this.user.bag = new Bag(10); 
          }) 
        Button(`ViewB: this.user = new User(new Bag(20))`) 
          .width(320) 
          .backgroundColor('#ff17a98d') 
          .margin(10) 
          .onClick(() => { 
            // @Trace修饰的字段具备深度观测能力,可以直接修改 
            this.user = new User(new Bag(20)); 
          }) 
      }.width('100%').height('100%') 
    }.onReady((context: NavDestinationContext) => { 
      this.pathStack = context.pathStack; 
    }) 
  } 
}

实现效果与状态管理V1一致。

场景三:对象数组监听

对象数组是指该数组的每一项元素都是对象的场景,该数据类型属于我们开发中最场景的一种场景,下面实现一个双层对象数据的场景示例,具体数据模型见下图。

AllList{}-->Outer[]--->Inner[]

基于状态变量实现复杂对象的状态监听-鸿蒙开发者社区

方案一:状态管理V1实现

在场景二中我们介绍了,状态管理V1在一层组件中只能监听对象下面的一层字段,因此这里需要分多层子组件来实现,具体结构是与数据模型保持一致,组件关系与数据模型对应图示如下:

基于状态变量实现复杂对象的状态监听-鸿蒙开发者社区

数据分层代码如下:

前面介绍了,因为无法观测多层对象的变化,因此我们只能将数据拆分成一层一层的来观测。

// model.ets 
/** 
 * 列表数据结构模型 
 */ 
export class AllList { 
  // 列表总数据源 
  items: Outer[] = [] 
  // 内层列表勾选状态下输入总金额 
  allAmount: string = '' 
 
  constructor(items: Outer[], allAmount: string) { 
    this.items = items 
    this.allAmount = allAmount 
  } 
} 
 
@Observed 
export class Outer { 
  // 外层label  类似于视频中的居民、企事业 
  public label: string = '' 
  //外层label展开状态,点击显示里层inners,再次点击隐藏inners 
  public expand: boolean = false 
  //内部数据源  类似视频中的每个用点户号 
  public inners: Inner[] = [] 
 
  constructor(label: string, inners: Inner[], expand?: boolean) { 
    this.label = label 
    this.inners = inners 
    this.expand = expand ? expand : false 
  } 
} 
 
@Observed 
export class Inner { 
  public id: string 
  // 户号名称 
  public innerName: string = '' 
  // 当前户号的勾选状态 
  public checkStatus: boolean = false 
  // 推荐金额 类似于视频重的按钮金额  ¥20/¥50/¥100/¥200 
  public btnMoney: string = '0' 
  // 输入框输入的金额  只能是数字、两位小数 
  public inputContent: string = '' 
 
  constructor(innerName: string) { 
    this.innerName = innerName 
    this.id = Math.random().toString() 
  } 
}

UI实现

然后在页面中使用的时候也是一层组件对应一层数据结构。

// index.ets 
import { Inner, Outer } from '../model/Model' 
import { OuterComponent } from './components/ListComponent' 
 
// 创建新实例并使用给定对象初始化 
let para: Record<string, number> = { 'allAmount': 0 }; 
let storage: LocalStorage = new LocalStorage(para); 
 
@Entry(storage) 
@Component 
struct Index { 
  // 首页Index对应AllList,这里没有直接使用上面的AllList对象,而是直接使用状态变量将两个字段分别观测了 
  @Provide AllList: Outer[] = [] 
  @LocalStorageLink('allAmount') allAmount: number = 0 
 
  aboutToAppear(): void { 
    // 初始化数据 
    this.AllList = [ 
      new Outer('居民', [new Inner('光谷理想城x栋x单元xxx'), new Inner('保利时代x栋x单元xxx'), new Inner('南湖名都x栋x单元xxx')]), 
      new Outer('企事业', [new Inner('光谷软件园x栋x楼xxxx'), new Inner('光谷金融港x栋x楼xxx'), new Inner('光谷智慧园x栋x楼xxx')]) 
    ] 
  } 
 
  build() { 
    Column() { 
      ... 
      List() { 
        ForEach(this.AllList, (outer: Outer) => { 
          if (outer.inners.length) { 
            // 遍历数组将对应的Outer对象传递给OuterComponent子组件,在子组件完成Outer对象的观测并同步给父组件 
            OuterComponent({ outer: outer }) 
          } 
        }, (item: Outer) => item.label) 
      } 
      ... 
    }.backgroundColor('#e9ecef').width('100%').height('100%') 
  } 
}
// ListComponent.ets 
import { Inner, Outer } from '../../model/Model' 
 
@Component 
export struct OuterComponent { 
  // 接收Index传进来的Outer对象,并使用@ObjectLink观测实现父子组件双向同步 
  @ObjectLink outer: Outer 
  @State arrowImg: Resource = $r('sys.media.ohos_ic_public_arrow_up') 
  ... 
  // 只展示核心代码,完整demo见最后附件 
  build() { 
    ListItemGroup({ header: this.itemHead(this.outer.label), style: ListItemGroupStyle.NONE }) { 
      ForEach(this.outer.inners, (inner: Inner, index: number) => { 
        ListItem({ style: ListItemStyle.NONE }) { 
          // 因为当前的Outer对象中还存在Inner对象数组,因此继续分层 
          InnerComponent({ inner: inner }) 
        } 
      }, (inner: Inner) => inner.innerName) 
    }.backgroundColor('#e9ecef') 
    .margin({ top: 16 }) 
  } 
} 
 
@Component 
struct InnerComponent { 
  // 接受OuterComponent传进来的Inner对象,并使用@ObjectLink观测实现父子组件双向同步 
  @ObjectLink inner: Inner 
 
  build() { 
    ... 
    // 只展示核心代码,完整demo见最后附件 
  } 
}

方案二:状态管理V2实现

这种方式对比方案一,无需对组件分层,只用在需要UI展示的字段上使用@Trace修饰器修饰即可,具体实现如下:

/*  
 列表数据结构模型 
 */ 
@ObservedV2 
class AllList { 
  // 列表总数据源 
  @Trace items: Outer[] = [] 
  // 内层列表勾选状态下输入总金额 
  @Trace allAmount: string = '' 
 
  constructor(items: Outer[], allAmount: string) { 
    this.items = items 
    this.allAmount = allAmount 
  } 
} 
 
@ObservedV2 
class Outer { 
  // 外层label  类似于视频中的居民、企事业 
  @Trace label: string = '' 
  //外层label展开状态,点击显示里层inners,再次点击隐藏inners 
  @Trace expand: boolean = false 
  //内部数据源  类似视频中的每个用点户号 
  @Trace inners: Inner[] = [] 
 
  constructor(label: string, inners: Inner[], expand?: boolean) { 
    this.label = label 
    this.inners = inners 
    this.expand = expand ? expand : false 
  } 
} 
 
@ObservedV2 
class Inner { 
  @Trace id: string 
  // 户号名称 
  @Trace innerName: string = '' 
  // 当前户号的勾选状态 
  @Trace checkStatus: boolean = false 
  // 推荐金额 类似于视频重的按钮金额  ¥20/¥50/¥100/¥200 
  @Trace btnMoney: string = '0' 
  // 输入框输入的金额  只能是数字、两位小数 
  @Trace inputContent: string = '' 
 
  constructor(innerName: string) { 
    this.innerName = innerName 
    this.id = Math.random().toString() 
  } 
} 
 
@Entry 
@Component 
struct IndexV2 { 
  // 数据初始化 
  AllList: AllList = new AllList([ 
    new Outer('居民', 
      [new Inner('xx小区x栋x单元xxx'), new Inner('xx小区x栋x单元xxx'), new Inner('xx小区x栋x单元xxx')]), 
    new Outer('企事业', 
      [new Inner('xx小区x栋x单元xxx'), new Inner('xx小区栋x单元xxx'), new Inner('xx小区x栋x单元xxx')]) 
  ], '0') 
 
  // ... 
  // 只展示数据监听核心代码,详细实现请看文章结尾附件 
 
  build() { 
    Column() { 
      List() { 
        // 因为@ObservedV2具备深入观测能力,使用时直接使用数据对象往下.即可 
        ForEach(this.AllList.items, (outer: Outer) => { 
          ListItemGroup({ header: this.itemHead(outer), style: ListItemGroupStyle.NONE }) { 
            ForEach(outer.inners, (inner: Inner, index: number) => { 
              ListItem({ style: ListItemStyle.NONE }) { 
                // ... 
                // 只展示数据监听核心代码,详细实现请看文章结尾附件 
              } 
            }, (inner: Inner) => inner.innerName) 
          }.backgroundColor('#e9ecef') 
          .margin({ top: 16 }) 
        }, (item: Outer) => item.label) 
      } 
      .width('90%').height('100%') 
    }.backgroundColor('#e9ecef').width('100%').height('100%') 
  } 
}

汇总对比

场景

状态管理V1

状态管理V2

简单对象

使用@State观测,在字段过多时建议结合@Track做最小化观测

使用@ObservedV2+@Trace观测,需要UI展示的字段使用@Trace修饰,同时如果涉及到整个对象替换的时候需要在组件中使用@Local修饰

复杂对象

使用@Observed+@ObjectLink观测,但是对应多层数据需要数据分层的同时还需要组件分层,通过这种方式虽然能够实现对嵌套类中属性变化的观测,但是当嵌套层级较深时,代码将会变得十分复杂,易用性差

@ObservedV2装饰器与@Trace装饰器用于装饰类以及类中的属性,使得被装饰的类和属性具有深度观测的能力,对比V1的实现更符合开发逻辑,易用性与代码可读性都更好


以上是针对上面三个场景对V1与V2做到一个对比,下面针对V1与V2的各类装饰器做一个完整对比:


状态管理V1

状态管理V2

对比

组件内状态

@State

@Local

状态管理V1使用@State定义类中的状态变量,@State装饰器能够从外部初始化,因此@State无法准确表达组件内部状态不能被外面修改的语义,而状态管理V2中的@Local无法从外传入初始化,因此能更准确的表达组件的内部状态

组件外部输入

@State/@Prop/@Link/@ObjectLink

@Param

状态管理V1存在多种可接受外部传入的装饰器,常用的有@State、@Prop、@Link、@ObjectLink。这些装饰器使用各有限制,不易区分,当使用不当时,还会导致性能问题。而状态管理V2只有@Param装饰器表示组件从外部传入的状态,并且可以实现与父组件@Local修饰的变量进行同步

状态变量修改监听

@Watch

@Monitor

@Watch无法实现对对象、数组中某一单个属性或数组项变化的监听,且无法获取变化之前的值。@Monitor装饰器实现对对象、数组中某一单个属性或数组项变化的监听,并且能够获取到变化之前的值


更多资料,请参考文档:状态管理V2

已于2024-8-1 16:30:03修改
收藏
回复
举报
回复
    相关推荐