前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >ArkTS-@Observed装饰器和@ObjectLink装饰器

ArkTS-@Observed装饰器和@ObjectLink装饰器

作者头像
酒楼
发布于 2023-07-05 08:21:22
发布于 2023-07-05 08:21:22
75400
代码可运行
举报
文章被收录于专栏:酒楼酒楼
运行总次数:0
代码可运行

@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装饰的数据为可读示例。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//允许@ObjectLink装饰的数据属性赋值
this.objLink.a=...
//不允许@ObjectLink装饰的数据自身赋值
this.objLink= ...

变量的传递/访问规则说明

@ObjectLink传递/访问

说明

从父组件初始化

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

与源对象同步

双向。

可以初始化子组件

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

观察变化和行为表现

观察的变化

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ClassA{
    public c: number;
    constructor(c: number){
        this.c = c;
    }
}
@Observed
class ClassB{
    public a: ClassA;
    public b: number;
    
    constructor(a: ClassA, b: number){
        this.a = a;
        this.b = b;
    }
}

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@ObjectLink b: ClassB

//赋值变化可以被观察到
this.b.a = new ClassA(5)
this.b.b = 5

//ClassA没有被@Observed装饰,其属性的变化观察不到
this.b.a.c = 5

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

  • 其属性的数值的变化,其中属性是指Object.keys(observedObject)返回的所有属性,实例请参考嵌套对象。
  • 如果数据源是数组,则可以观察到数组item的替换,如果数据源是class,可观察到class的属性的变化,实例请参考对象数组。

框架行为

1.初始渲染:

​ a.@Observed装饰的class的实例会被不透明的代理对象包装,代理了class上的属性的setter和getter方法

​ b.子组件中@ObjectLink装饰的从父组件初始化,接受被@Observed装饰的class的实例,@ObjectLink的包装类会将自己注册给@Observed class。

2.属性更新:当@Observed装饰的class属性改变时,会走到代理的setter和getter,然后遍历依赖它的@ObjectLink包装类,通知数据更新。

使用场景

嵌套对象

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//objectLinkNestedObjects.ets
let NextID: number = 1;

@Observed
class ClassA{
    public id: number;
    public c: number;
    
    constructor(c: number){
        this.id = NextID++;
        this.c = c;
    }
}

@Observed
class ClassB{
    public a: ClassA;
    
    constructor(a: ClassA){
        this.a = a;
    }
}

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
struct ViewA{
    label: string = 'ViewA1';
    @ObjectLink a: ClassA;
    
    build(){
        Row(){
            Button(`ViewA [${this.label}] this.a.c=${this.a.c}+1`)
            	.onClick(()=>{
                this.a.c +=1;
            })
        }
    }
}

@Entry
@Component
struct ViewB{
    @State b: ClassB = new ClassB(new ClassA(0));
    
    build(){
        Column(){
            ViewA({label: 'ViewA #1',a: this.b.a})
            ViewA({label: 'ViewA #2',a: this.b.a})
            Button(`ViewB: this.b.a.c+=1`)
            	.onClick(()=>{
                this.b.a.c +=1;
            })
            
            Button(`ViewB: this.b.a =new ClassA(0)`)
            	.onClick(()=>{
                this.b.a = new ClassA(0);
            })
            
            Button(`ViewB: this.b = new ClassB(ClassA(0))`)
            	.onClick(()=>{
                this.b = new ClassB(new ClassA(0));
            })
        }
    }
}

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(…)是不允许的,因为一旦赋值操作发生,指向数据源的引用将被重置,同步将被打断。

对象数组

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
struct ViewA{
    //子组件ViewA的@ObjectLink的类型是ClassA
    @ObjectLink a: ClassA;
    label: string = 'ViewA1';
    
    build(){
        Row(){
            Button(`ViewA[${this.label}] this.a.c = ${this.a.c}+1`)
            	.onClick(()=>{
                this.a.c +=1;
                
            })
        }
    }
}

@Entry
@Component
struct ViewB{
    //ViewB中有@State装饰的ClassA[]
    @State arrA: ClassA[]  = [new ClassA(0), new ClassA(0)];
    
    build(){
        Column(){
            ForEach(this.arrA,
                   (item) =>{
                ViewA({label: `#${item.id}`,a:item})
            },
                   (item) =>item.id.toString())
            
            //使用@State装饰的数组的数组项初始化@ObjectLink,其中数组项是被@Observed装饰的ClassA的实例
            ViewA({label: `ViewA this.arrA[first]`,a: this.arrA[0]})
            ViewA({label: `ViewA this.arrA[last]`,a: this.arrA.length-1]})
            
            Button(`ViewB: reset array`)
            	.onClick(()=>{
                this.arrA = [new ClassA(0),new ClassA(0)];
            })
            
            Button(`ViewB: push`)
            	.onClick(()=>{
                this.arrA.push(new ClassA(0))
            })
            
            Button(`ViewB: shift`)
            	.onClick(()=>{
                this.arrA.shift()
            })
            
            Button(`ViewB: chg item property in middle`)
            	.onClick(()=>{
                this.arrA[Math.floor(this.arrA.length/2)].c = 10;
            })
            
            Button(`ViewB: chg item property in middle`)
            	.onClick(()=>{
                this.arrA[Math.floor(this.arrA.length/2)]=new ClassA(11);
            })
        }
    }
}
  • this.arrA[Math.floor(this.arrA.length/2)] = new ClassA(…): 该状态变量的改变触发2次更新:
    • ForEach: 数组项的赋值导致ForEach的itemGenerator被修改,因此数组项被识别为有更改,ForEach的item builder将执行,创建新的ViewA组件实例。
    • ViewA({label: ViewA this.arrA[first],a:this.arrA[0]}):上述更改改变了数组中第一个元素,所以绑定this.arrA[0] 的ViewA组将被更新;
  • this.arrA.push(new ClassA(0)):将触发2次不同效果的更新:
    • ForEach:将添加的ClassA对象对于ForEach是未知的itemGenerator,ForEach的itembuilder将被执行,创建新的ViewA组件实例。
    • 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的子类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Observed
class StringArray extends Array<String>{
    
}

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Observed
class StringArray extends Array<String>{
    
}
@Component
struct ItemPage{
    @ObjectLink itemArr: StringArray;
    
    build(){
        Row(){
            Text('ItemPage')
            	.width(100).height(100)
            
            ForEach(this.itemArr,
                   item =>{
                Text(item)
                	.width(100).height(100)
            },
                   item => item)
        }
    }
}

@Entry
@Componnet
struct IndexPage{
    @State arr: Array<StringArray> = [new StringArray(),new StringArray(),new StringArray()];
    
    build(){
        Column(){
            ItemPage({itemArr: this.arr[0]})
            ItemPage({itemArr: this.arr[1]})
            ItemPage({itemArr: this.arr[2]})
            
            Divider()
            
            ForEach(this.arr,
                   itemArr =>{
                ItemPage({itemArr:itemArr})
            },
                   itemArr=>itemArr[0])
            
            Divider()
            
            Button('update')
            	.onClick(()=>{
                console.error('Update all items in arr');
                if(this.arr[0][0]!== undefined){
                    //正常情况下需要有一个真实的ID来与ForEach一起使用,但此处没有
                    //因此需要确保推送的字符串是唯一的。
                    this.arr[0].push(`${this.arr[0].slice(-1).pop()}${this.arr[0].slice(-1).pop()}`);
                    this.arr[1].push(`${this.arr[1].slice(-1).pop()}${this.arr[1].slice(-1).pop()}`);
                    this.arr[2].push(`${this.arr[2].slice(-1).pop()}${this.arr[2].slice(-1).pop()}`);
                }else{
                    this.arr[0].push('Hello');
                    this.arr[1].push('World');
                    this.arr[2].push('!');
                }
            })
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-06-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
我们一起来学RabbitMQ 三:RabbiMQ 死信队列,延迟队列,持久化等知识点
fanout exchange 可以做成备份的交换机,因为 fanout 的消息是广播的方式
阿兵云原生
2023/02/16
2810
rabbitmq之rabbitmq工作模型与Java编程(一)
1、跨系统的异步通信 人民银行二代支付系统,使用重量级消息队列 IBM MQ,异步,解耦,削峰都有体现。 2、应用内的同步变成异步 秒杀:自己发送给自己 3、基于Pub/Sub模型实现的事件驱动 放款失败通知、提货通知、购买碎屏保 系统间同步数据 摒弃ELT(比如全量同步商户数据); 摒弃API(比如定时增量获取用户、获取产品,变成增量广播)。 4、利用RabbitMQ实现事务的最终一致性
周杰伦本人
2022/10/25
3970
rabbitmq之rabbitmq工作模型与Java编程(一)
RabbitMQ 基础概念与架构设计及工作机制学习总结
MQ全称为Message Queue,即消息队列. 它也是一个队列,遵循FIFO原则 。RabbitMQ则是一个开源的消息中间件,由erlang语言开发,基于AMQP协议实现的一个软件产品,提供应用程序之间的通信方法,在分布式系统开发中广泛应用。
授客
2024/11/21
4980
RabbitMQ 基础概念与架构设计及工作机制学习总结
消息队列——RabbitMQ的基本使用及高级特性
Rabbit是基于AMQP协议并使用Erlang开发的开源消息队列中间件,它支持多种语言的客户端,也是目前市面上使用比较广泛的一种消息队列,因此学习并掌握它是非常有必要的。本文主要基于Java客户端进行讲解,不涉及环境搭建部分。
夜勿语
2020/09/07
8080
Rabbitmq小书
1.生产者(Publisher): 发布消息到RabbitMQ中的交换机(Exchange)上
大忽悠爱学习
2022/10/04
3.4K0
Rabbitmq小书
Java开发面试--RabbitMQ专区3
RabbitMQ是一个消息中间件,本身并不支持分布式事务。但可以通过以下几种方式来实现分布式事务:
忆愿
2024/09/14
740
Java开发面试--RabbitMQ专区3
消息队列技术选型:这 7 种消息场景一定要考虑!
我们在做消息队列的技术选型时,往往会结合业务场景进行考虑。今天来聊一聊消息队列可能会用到的 7 种消息场景。
jinjunzhu
2023/09/27
6000
消息队列技术选型:这 7 种消息场景一定要考虑!
RabbitMQ
RabbitMQ属于中间件的一种,其实很多东西都是中间件比如说mysql redis都是的 其实中间件是一种概念,只要是实现软件和软件之间沟通连接的软件都可以叫做中间件
xiaozhangStu
2023/05/04
1K0
RabbitMQ 26问,基本涵盖了面试官必问的面试题
**Connection** **极大减少了操作系统建立** **TCP connection** **的开销**
小熊学Java
2022/09/04
5550
高性能消息队列中间件MQ_part2
之前我们使用原生JAVA操作RabbitMQ较为繁琐,接下来我们使用SpringBoot整合RabbitMQ,简化代码编写。
天天Lotay
2023/02/16
4370
高性能消息队列中间件MQ_part2
RabbitMq 总结
不依赖于路由键的匹配规则路由消息,根据发送的消息内容headers属性进行完全匹配(键值对形式)。性能差,基本不使用。
leon公众号精选
2022/04/27
4710
RabbitMq 总结
RabbitMQ---延迟队列,整合springboot
延时队列,队列内部是有序的,最重要的特性就体现在它的延时属性上,延时队列中的元素是希望在指定时间到了以后或之前取出和处理,简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列。
大忽悠爱学习
2021/12/07
6550
RabbitMQ---延迟队列,整合springboot
RabbitMQ 超详细入门篇
本人使用的是 阿里云服务器 没有的话也可以使用虚拟机… 事先使用连接工具上传了文件
Java_慈祥
2024/08/06
1.7K0
RabbitMQ 超详细入门篇
消息队列-RabbitMQ
交换机有四种类型:direct exchange、topic exchange、fanout exchange、headers exchange。
lpe234
2021/03/04
1.7K0
基于RabbitMQ实现延迟队列--PHP版
场景一:物联网系统经常会遇到向终端下发命令,如果命令一段时间没有应答,就需要设置成超时。
码农编程进阶笔记
2022/04/08
8450
基于RabbitMQ实现延迟队列--PHP版
RabbitMQ 高频考点
比如有一个订单系统,还要一个库存系统,用户下订单后要调用库存系统来处理,直接调用话,库存系统出现问题咋办呢?
sowhat1412
2022/09/20
6800
RabbitMQ 高频考点
Rabbitmq业务难点
消息生产者如果向交换机发送了一个无法被路由到任何队列上的消息,那么此时交换机会判断消息的mandatory属性值:
大忽悠爱学习
2023/02/26
8420
Rabbitmq业务难点
RabbitMQ
​ MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出,只不过队列中存放的内容是 message 而已,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ 是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。使用了 MQ 之后,消息发送上游只需要依赖 MQ,不用依赖其他服务。
OY
2022/03/21
1.8K0
RabbitMQ
RabbitMQ 延迟队列
延时队列,队列内部是有序的,最重要的特性就体现在它的延时属性上,延时队列中的元素是希望 在指定时间到了以后或之前取出和处理,简单来说,延时队列就是用来存放需要在指定时间被处理的 元素的队列。
用户9615083
2022/12/25
6460
RabbitMQ 延迟队列
RabbitMQ高级面试题
在生产者投递消息时指定mandatory或者imrnediate参数设为 true 时,RabbitMQ 会把无法投递的消息通过Basic.Return 命令将消息返回给生产者,此时生产者需要调用channel.addReturnListener 来添加 ReturnListener 监昕器实现监听投递失败的消息
Java学习录
2019/07/01
3.9K0
相关推荐
我们一起来学RabbitMQ 三:RabbiMQ 死信队列,延迟队列,持久化等知识点
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • @Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化
    • 概述
    • 限制条件
    • 装饰器说明
    • 变量的传递/访问规则说明
    • 观察变化和行为表现
      • 观察的变化
    • 框架行为
    • 使用场景
      • 嵌套对象
    • 对象数组
    • 二维数组
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文