首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

鸿蒙HarmonyOS开发框架—ArkTS语言(状态管理 四)

  • 25-02-22 03:20
  • 4213
  • 7786
blog.csdn.net

@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

上文所述的装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink装饰器。

概述

@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:

  • 被@Observed装饰的类,可以被观察到属性的变化;
  • 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中是属性,这个属性同样也需要被@Observed装饰。
  • 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。
限制条件

使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。

装饰器说明

@Observed类装饰器

说明

装饰器参数

无

类装饰器

装饰class。需要放在class的定义前,使用new创建类对象。

@ObjectLink变量装饰器

说明

装饰器参数

无

同步类型

不与父组件中的任何类型同步变量。

允许装饰的变量类型

必须为被@Observed装饰的class实例,必须指定类型。 不支持简单类型,可以使用@Prop。 @ObjectLink的属性是可以改变的,但是变量的分配是不允许的,也就是说这个装饰器装饰变量是只读的,不能被改变。

被装饰变量的初始值

不允许。

@ObjectLink装饰的数据为可读示例。

  1. // 允许@ObjectLink装饰的数据属性赋值
  2. this.objLink.a= ...
  3. // 不允许@ObjectLink装饰的数据自身赋值
  4. this.objLink= ...

复制

说明 @ObjectLink装饰的变量不能被赋值,如果要使用赋值操作,请使用@Prop。

  • @Prop装饰的变量和数据源的关系是是单向同步,@Prop装饰的变量在本地拷贝了数据源,所以它允许本地更改,如果父组件中的数据源有更新,@Prop装饰的变量本地的修改将被覆盖;
  • @ObjectLink装饰的变量和数据源的关系是双向同步,@ObjectLink装饰的变量相当于指向数据源的指针。如果一旦发生@ObjectLink装饰的变量的赋值,则同步链将被打断。
变量的传递/访问规则说明

@ObjectLink传递/访问

说明

从父组件初始化

必须指定。 初始化@ObjectLink装饰的变量必须同时满足以下场景: 类型必须是@Observed装饰的class。初始化的数值需要是数组项,或者class的属性。同步源的class或者数组必须是@State,@Link,@Provide,@Consume或者@ObjectLink装饰的数据。

与源对象同步

双向。

可以初始化子组件

允许,可用于初始化常规变量、@State、@Link、@Prop、@Provide

  • 类型必须是@Observed装饰的class。
  • 初始化的数值需要是数组项,或者class的属性。
  • 同步源的class或者数组必须是@State,@Link,@Provide,@Consume或者@ObjectLink装饰的数据。

与源对象同步 双向。 可以初始化子组件 允许,可用于初始化常规变量、@State、@Link、@Prop、@Provide

图1 初始化规则图示

观察变化和行为表现
观察的变化

@Observed装饰的类,如果其属性为非简单类型,比如class、Object或者数组,也需要被@Observed装饰,否则将观察不到其属性的变化。

  1. class ClassA {
  2. public c: number;
  3. constructor(c: number) {
  4. this.c = c;
  5. }
  6. }
  7. @Observed
  8. class ClassB {
  9. public a: ClassA;
  10. public b: number;
  11. constructor(a: ClassA, b: number) {
  12. this.a = a;
  13. this.b = b;
  14. }
  15. }

复制

以上示例中,ClassB被@Observed装饰,其成员变量的赋值的变化是可以被观察到的,但对于ClassA,没有被@Observed装饰,其属性的修改不能被观察到。

  1. @ObjectLink b: ClassB
  2. // 赋值变化可以被观察到
  3. this.b.a = new ClassA(5)
  4. this.b.b = 5
  5. // ClassA没有被@Observed装饰,其属性的变化观察不到
  6. this.b.a.c = 5

复制

@ObjectLink:@ObjectLink只能接收被@Observed装饰class的实例,可以观察到:

  • 其属性的数值的变化,其中属性是指Object.keys(observedObject)返回的所有属性。
  • 如果数据源是数组,则可以观察到数组item的替换,如果数据源是class,可观察到class的属性的变化。
框架行为
  1. 初始渲染:
    1. @Observed装饰的class的实例会被不透明的代理对象包装,代理了class上的属性的setter和getter方法
    2. 子组件中@ObjectLink装饰的从父组件初始化,接收被@Observed装饰的class的实例,@ObjectLink的包装类会将自己注册给@Observed class。
  2. 属性更新:当@Observed装饰的class属性改变时,会走到代理的setter和getter,然后遍历依赖它的@ObjectLink包装类,通知数据更新。
使用场景
嵌套对象

以下是嵌套类对象的数据结构。

  1. // objectLinkNestedObjects.ets
  2. let NextID: number = 1;
  3. @Observed
  4. class ClassA {
  5. public id: number;
  6. public c: number;
  7. constructor(c: number) {
  8. this.id = NextID++;
  9. this.c = c;
  10. }
  11. }
  12. @Observed
  13. class ClassB {
  14. public a: ClassA;
  15. constructor(a: ClassA) {
  16. this.a = a;
  17. }
  18. }

复制

以下组件层次结构呈现的是此数据结构

  1. @Component
  2. struct ViewA {
  3. label: string = 'ViewA1';
  4. @ObjectLink a: ClassA;
  5. build() {
  6. Row() {
  7. Button(`ViewA [${this.label}] this.a.c=${this.a.c} +1`)
  8. .onClick(() => {
  9. this.a.c += 1;
  10. })
  11. }
  12. }
  13. }
  14. @Entry
  15. @Component
  16. struct ViewB {
  17. @State b: ClassB = new ClassB(new ClassA(0));
  18. build() {
  19. Column() {
  20. ViewA({ label: 'ViewA #1', a: this.b.a })
  21. ViewA({ label: 'ViewA #2', a: this.b.a })
  22. Button(`ViewB: this.b.a.c+= 1`)
  23. .onClick(() => {
  24. this.b.a.c += 1;
  25. })
  26. Button(`ViewB: this.b.a = new ClassA(0)`)
  27. .onClick(() => {
  28. this.b.a = new ClassA(0);
  29. })
  30. Button(`ViewB: this.b = new ClassB(ClassA(0))`)
  31. .onClick(() => {
  32. this.b = new ClassB(new ClassA(0));
  33. })
  34. }
  35. }
  36. }

复制

ViewB中的事件句柄:

  • this.b.a = new ClassA(0) 和this.b = new ClassB(new ClassA(0)): 对@State装饰的变量b和其属性的修改。
  • this.b.a.c = ... :该变化属于第二次的变化,@State无法观察到第二层的变化,但是ClassA被@Observed装饰,ClassA的属性c的变化可以被@ObjectLink观察到。

ViewA中的事件句柄:

  • this.a.c += 1:对@ObjectLink变量a的修改,将触发Button组件的刷新。@ObjectLink和@Prop不同,@ObjectLink不拷贝来自父组件的数据源,而是在本地构建了指向其数据源的引用。
  • @ObjectLink变量是只读的,this.a = new ClassA(...)是不允许的,因为一旦赋值操作发生,指向数据源的引用将被重置,同步将被打断。
对象数组

对象数组是一种常用的数据结构。以下示例展示了数组对象的用法。

  1. @Component
  2. struct ViewA {
  3. // 子组件ViewA的@ObjectLink的类型是ClassA
  4. @ObjectLink a: ClassA;
  5. label: string = 'ViewA1';
  6. build() {
  7. Row() {
  8. Button(`ViewA [${this.label}] this.a.c = ${this.a.c} +1`)
  9. .onClick(() => {
  10. this.a.c += 1;
  11. })
  12. }
  13. }
  14. }
  15. @Entry
  16. @Component
  17. struct ViewB {
  18. // ViewB中有@State装饰的ClassA[]
  19. @State arrA: ClassA[] = [new ClassA(0), new ClassA(0)];
  20. build() {
  21. Column() {
  22. ForEach(this.arrA,
  23. (item) => {
  24. ViewA({ label: `#${item.id}`, a: item })
  25. },
  26. (item) => item.id.toString()
  27. )
  28. // 使用@State装饰的数组的数组项初始化@ObjectLink,其中数组项是被@Observed装饰的ClassA的实例
  29. ViewA({ label: `ViewA this.arrA[first]`, a: this.arrA[0] })
  30. ViewA({ label: `ViewA this.arrA[last]`, a: this.arrA[this.arrA.length-1] })
  31. Button(`ViewB: reset array`)
  32. .onClick(() => {
  33. this.arrA = [new ClassA(0), new ClassA(0)];
  34. })
  35. Button(`ViewB: push`)
  36. .onClick(() => {
  37. this.arrA.push(new ClassA(0))
  38. })
  39. Button(`ViewB: shift`)
  40. .onClick(() => {
  41. this.arrA.shift()
  42. })
  43. Button(`ViewB: chg item property in middle`)
  44. .onClick(() => {
  45. this.arrA[Math.floor(this.arrA.length / 2)].c = 10;
  46. })
  47. Button(`ViewB: chg item property in middle`)
  48. .onClick(() => {
  49. this.arrA[Math.floor(this.arrA.length / 2)] = new ClassA(11);
  50. })
  51. }
  52. }
  53. }

复制

  • this.arrA[Math.floor(this.arrA.length/2)] = new ClassA(..) :该状态变量的改变触发2次更新:
    1. ForEach:数组项的赋值导致ForEach的itemGenerator被修改,因此数组项被识别为有更改,ForEach的item builder将执行,创建新的ViewA组件实例。
    2. ViewA({ label: `ViewA this.arrA[first]`, a: this.arrA[0] }):上述更改改变了数组中第一个元素,所以绑定this.arrA[0]的ViewA将被更新;
  • this.arrA.push(new ClassA(0)) : 将触发2次不同效果的更新:
    1. ForEach:新添加的ClassA对象对于ForEach是未知的itemGenerator,ForEach的item builder将执行,创建新的ViewA组件实例。
    2. ViewA({ label: `ViewA this.arrA[last]`, a: this.arrA[this.arrA.length-1] }):数组的最后一项有更改,因此引起第二个ViewA的实例的更改。对于ViewA({ label: `ViewA this.arrA[first]`, a: this.arrA[0] }),数组的更改并没有触发一个数组项更改的改变,所以第一个ViewA不会刷新。
  • this.arrA[Math.floor(this.arrA.length/2)].c:​​​​​​​@State无法观察到第二层的变化,但是ClassA被@Observed装饰,ClassA的属性的变化将被@ObjectLink观察到。
二维数组

使用@Observed观察二维数组的变化。可以声明一个被@Observed装饰的继承Array的子类。

  1. @Observed
  2. class StringArray extends Array {
  3. }

复制

使用new StringArray()来构造StringArray的实例,new运算符使得@Observed生效,@Observed观察到StringArray的属性变化。

声明一个从Array扩展的类class StringArray extends Array {},并创建StringArray的实例。@Observed装饰的类需要使用new运算符来构建class实例。

  1. @Observed
  2. class StringArray extends Array<String> {
  3. }
  4. @Component
  5. struct ItemPage {
  6. @ObjectLink itemArr: StringArray;
  7. build() {
  8. Row() {
  9. Text('ItemPage')
  10. .width(100).height(100)
  11. ForEach(this.itemArr,
  12. item => {
  13. Text(item)
  14. .width(100).height(100)
  15. },
  16. item => item
  17. )
  18. }
  19. }
  20. }
  21. @Entry
  22. @Component
  23. struct IndexPage {
  24. @State arr: Array<StringArray> = [new StringArray(), new StringArray(), new StringArray()];
  25. build() {
  26. Column() {
  27. ItemPage({ itemArr: this.arr[0] })
  28. ItemPage({ itemArr: this.arr[1] })
  29. ItemPage({ itemArr: this.arr[2] })
  30. Divider()
  31. ForEach(this.arr,
  32. itemArr => {
  33. ItemPage({ itemArr: itemArr })
  34. },
  35. itemArr => itemArr[0]
  36. )
  37. Divider()
  38. Button('update')
  39. .onClick(() => {
  40. console.error('Update all items in arr');
  41. if (this.arr[0][0] !== undefined) {
  42. // 正常情况下需要有一个真实的ID来与ForEach一起使用,但此处没有
  43. // 因此需要确保推送的字符串是唯一的。
  44. this.arr[0].push(`${this.arr[0].slice(-1).pop()}${this.arr[0].slice(-1).pop()}`);
  45. this.arr[1].push(`${this.arr[1].slice(-1).pop()}${this.arr[1].slice(-1).pop()}`);
  46. this.arr[2].push(`${this.arr[2].slice(-1).pop()}${this.arr[2].slice(-1).pop()}`);
  47. } else {
  48. this.arr[0].push('Hello');
  49. this.arr[1].push('World');
  50. this.arr[2].push('!');
  51. }
  52. })
  53. }
  54. }
  55. }

最后,为了能让大家更好的去学习提升鸿蒙 (Harmony OS) 开发技术,小编连夜整理了一份30个G纯血版学习资料(含视频、电子书、学习文档等)以及一份在Github上持续爆火霸榜的《纯血版华为鸿蒙 (Harmony OS)开发手册》(共计890页),希望对大家有所帮助。

纯血版鸿蒙 HarmonyOS 4.0 视频学习资料

 需要以上视频学习资料小伙伴

请点击→纯血版全套鸿蒙HarmonyOS学习资料


《纯血版华为鸿蒙 (Harmony OS)开发手册》

这份手册涵盖了当前鸿蒙 (Harmony OS) 开发技术必掌握的核心知识点

纯血版鸿蒙 (Harmony OS)开发手册部分精彩内容

HarmonyOS 概念:

  • 系统定义
  • 技术架构
  • 技术特性
  • 系统安全

如何快速入门?

  • 基本概念
  • 构建第一个ArkTS应用
  • 构建第一个JS应用
  • ……


开发基础知识: 

  • 应用基础知识
  • 配置文件
  • 应用数据管理
  • 应用安全管理
  • 应用隐私保护
  • 三方应用调用管控机制
  • 资源分类与访问
  • 学习ArkTS语言
  • ……

基于ArkTS 开发:

  • Ability开发
  • UI开发
  • 公共事件与通知
  • 窗口管理
  • 媒体
  • 安全
  • 网络与链接
  • 电话服务
  • 数据管理
  • 后台任务(Background Task)管理
  • 设备管理
  • 设备使用信息统计
  • DFX
  • 国际化开发
  • 折叠屏系列
  • .……

获取以上文中提到的这份纯血版鸿蒙 (Harmony OS) 开发资料的小伙伴 

请点击→纯血版全套鸿蒙HarmonyOS学习资料


?写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing?,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新VIP学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

最新鸿蒙Next全套学习资料请扫码
微信名片
注:本文转载自blog.csdn.net的蜀道山QAQ的文章"https://blog.csdn.net/shudaoshanQAQ/article/details/135637361"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top