当前位置: 首页 > news >正文

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

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

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

说明

从API version 9开始,这两个装饰器支持在ArkTS卡片中使用。

概述

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

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

说明

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

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

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

@ObjectLink传递/访问说明
从父组件初始化必须指定。初始化@ObjectLink装饰的变量必须同时满足以下场景:类型必须是@Observed装饰的class。初始化的数值需要是数组项,或者class的属性。同步源的class或者数组必须是@State,@Link,@Provide,@Consume或者@ObjectLink装饰的数据。同步源是数组项的示例请参考对象数组。初始化的class的示例请参考嵌套对象。
与源对象同步双向。
可以初始化子组件允许,可用于初始化常规变量、@State、@Link、@Prop、@Provide

图1 初始化规则图示

img

观察变化和行为表现

观察的变化

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

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装饰,其属性的修改不能被观察到。

@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. 初始渲染:
    1. @Observed装饰的class的实例会被不透明的代理对象包装,代理了class上的属性的setter和getter方法
    2. 子组件中@ObjectLink装饰的从父组件初始化,接收被@Observed装饰的class的实例,@ObjectLink的包装类会将自己注册给@Observed class。
  2. 属性更新:当@Observed装饰的class属性改变时,会走到代理的setter和getter,然后遍历依赖它的@ObjectLink包装类,通知数据更新。

使用场景

嵌套对象

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

// 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;}
}

以下组件层次结构呈现的是嵌套类对象的数据结构。

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

对象数组

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

@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[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次更新:
    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的子类。

@Observed
class StringArray extends Array<String> {
}

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

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

@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
@Component
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('!');}})}}
}

相关文章:

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

Observed装饰器和ObjectLink装饰器&#xff1a;嵌套类对象属性变化 上文所述的装饰器仅能观察到第一层的变化&#xff0c;但是在实际应用开发中&#xff0c;应用会根据开发需要&#xff0c;封装自己的数据模型。对于多层嵌套的情况&#xff0c;比如二维数组&#xff0c;或者数…...

x-cmd pkg | jless - 受 Vim 启发的命令行 JSON 查看器

目录 简介首次用户功能特点类似工具与竞品进一步探索 简介 jless 是一个命令行 JSON 查看器&#xff0c;设计用于读取、探索和搜索 JSON 数据。可以使用它来替代 less 、 jq 、 cat 以及您当前用于查看 JSON 文件的编辑器的任何组合。它是用 Rust 编写的&#xff0c;可以作为单…...

【HuggingFace Transformer库学习笔记】基础组件学习:Datasets

基础组件——Datasets datasets基本使用 导入包 from datasets import *加载数据 datasets load_dataset("madao33/new-title-chinese") datasetsDatasetDict({train: Dataset({features: [title, content],num_rows: 5850})validation: Dataset({features: [titl…...

[机缘参悟-126] :实修 - 从系统论角度理解自洽的人生:和谐、稳定,不拧巴,不焦虑,不纠结

目录 一、从系统论理解自洽 1.1 什么是系统 1.2 什么是自洽 1.3 什么是不自洽 1.4 为什么要自洽 1.5 不自洽的系统面临的挑战 二、人生需要自洽 2.1 人生自洽的意义 2.2 一个不自洽的人生会怎么样&#xff1f; 2.3 不自洽的特征 2.4 不自洽的人没有稳定的人格 三、…...

慢 SQL 的优化思路

分析慢 SQL 如何定位慢 SQL 呢&#xff1f; 可以通过 slow log 来查看慢SQL&#xff0c;默认的情况下&#xff0c;MySQL 数据库是不开启慢查询日志&#xff08;slow query log&#xff09;。所以我们需要手动把它打开。 查看下慢查询日志配置&#xff0c;我们可以使用 show …...

强化学习(一)简介

强化学习这一概念在历史上来源于行为心理学&#xff0c;来描述生物为了趋利避害而改变自己行为的学习过程。人类学习的过程其实就是为达到某种目的不断地与环境进行互动试错&#xff0c;比如婴儿学习走路。强化学习算法探索了一种从交互中学习的计算方法。 1、强化学习 强化学…...

外贸常用网站

外贸常用网站 网站阿里巴巴国际站阿里巴巴国内站Aliexpress 速卖通shopifyAmazon 亚马逊k3 开山女鞋网bao66 牛包包网爱搜鞋k3 开山网(女鞋)新款网(男女鞋)搜款网(男女衣服)17zwd(女装)17zwd(女装) 物流yunexpress 云途物流 其他amz123 跨境卖家导航amz520 跨境卖家导航 网站 …...

Android中集成FFmpeg及NDK基础知识

前言 在日常App开发中,难免有些功能是需要借助NDK来完成的,比如现在常见的音视频处理等,今天就以ffmpeg入手,来学习下Android NDK开发的套路. JNI和NDK 很多人并不清除JNI和NDK的概念,经常搞混这两样东西,先来看看它们各自的定义吧. JNI和NDK 很多人并不清除JNI和NDK的概念…...

1.13寒假集训

晚上兼职下班回来才有时间写题&#xff0c;早上根本起不来 A: 解题思路&#xff1a;我第一开始以为只要满足两个red以上的字母数量就行&#xff0c;但是过不了&#xff0c;后面才发现是red字符串&#xff0c;直接三个三个判断就行。 下面是c代码&#xff1a; #include<io…...

删除排序链表中的重复元素

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是出于什么原因&#xff0c;算法学习需要持续保持。 题目描述 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只…...

echarts的dispatchAction

触发图表行为&#xff0c;通过dispatchAction触发。例如图例开关legendToggleSelect, 数据区域缩放dataZoom&#xff0c;显示提示框showTip等等。 官网&#xff1a;echarts (在 ECharts 中主要通过 on 方法添加事件处理函数。) events&#xff1a; ECharts 中的事件分为两种…...

Java IO学习和总结(超详细)

一、理解 I/O 是输入和输出的简写&#xff0c;指的是数据在计算机内部和外部设备之间的流动。简单来说&#xff0c;当你从键盘输入数据、从鼠标选择操作&#xff0c;或者在屏幕上看到图像&#xff0c;这些都是 I/O 操作。它就像是计算机与外部世界沟通的桥梁&#xff0c;没有 I…...

mysql忘记root密码后怎么重置

mysql忘记root密码后重置方法【windows版本】 重置密码步骤停掉mysql服务跳过密码进入数据库在user表中重置密码使用新密码登录mysql到此&#xff0c;密码就成功修改了&#xff0c;完结&#xff0c;撒花~ 重置密码步骤 当我们忘记mysql的密码时&#xff0c;连接mysql会报这样的…...

计算机图形学作业:三维线段的图形变换

1. 将三维空间某线段 P1P2进行如下的操作&#xff0c;请按要求回答问题&#xff1a; &#xff08;1&#xff09; 沿 X 轴、Y 轴和 Z 轴分别平移 dx、dy 和 dz 的长度&#xff0c;给出相应的变换矩阵。 变换矩阵为&#xff1a; T100001000010dxdydz1 &#xff08;2&#xff09…...

Linux mren命令教程:批量重命名文件(附实际操作案例和注意事项)

Linux mren命令介绍 mren&#xff08;全称multiple rename&#xff09;&#xff0c;它是用来对多个文件进行重命名的工具。这个命令在一次操作中可以批量改变多个文件的名称&#xff0c;特别是在需要对大量文件进行重命名时&#xff0c;mren将节省大量的时间和努力。 Linux m…...

LLVM系列(1): 在微软Visual Studio下编译LLVM

参考链接&#xff1a; Getting Started with the LLVM System using Microsoft Visual Studio — LLVM 18.0.0git documentation 1.安装visualstudio&#xff0c;版本需要大于vs2019 本机环境已安装visual studio2022&#xff0c;省略 2安装Makefile&#xff0c;版本需要大…...

分布式系统的三字真经CAP

文章目录 前言C&#xff08;Consistency 数据一致性&#xff09;A&#xff08;Availability 服务可用性&#xff09;P&#xff08;Partition Tolerance 分区容错性&#xff09;CAP理论最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;我一起探索一下分布式系统的三字真经C…...

大模型背景下计算机视觉年终思考小结(一)

1. 引言 在过去的十年里&#xff0c;出现了许多涉及计算机视觉的项目&#xff0c;举例如下&#xff1a; 使用射线图像和其他医学图像领域的医学诊断应用使用卫星图像分析建筑物和土地利用率相关应用各种环境下的目标检测和跟踪&#xff0c;如交通流统计、自然环境垃圾检测估计…...

Modbus协议学习第一篇之基础概念

什么是“协议” 大白话解释&#xff1a;协议是用来正确传递消息数据而设立的一种规则。传递消息的双方&#xff08;两台计算机&#xff09;在通信时遵循同一种协议&#xff0c;即可理解彼此传递的消息数据。 Modbus协议模型 Modbus协议模型较为简单&#xff0c;使用一种称为应用…...

gem5学习(12):理解gem5 统计信息和输出——Understanding gem5 statistics and output

目录 一、config.ini 二、config.json 三、stats.txt 官方教程&#xff1a;gem5: Understanding gem5 statistics and output 在运行 gem5 之后&#xff0c;除了仿真脚本打印的仿真信息外&#xff0c;还会在根目录中名为 m5out 的目录中生成三个文件&#xff1a; config.i…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...