HarmonyOS—@Observed装饰器和@ObjectLink嵌套类对象属性变化
@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化
概述
@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:
- 被@Observed装饰的类,可以被观察到属性的变化;
- 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class
object中的属性,这个属性同样也需要被@Observed装饰。 - 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。
限制条件
- 使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。
- @ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。
装饰器说明
@ObjectLink装饰的数据为可读示例。
// 允许@ObjectLink装饰的数据属性赋值
this.objLink.a= ...
// 不允许@ObjectLink装饰的数据自身赋值
this.objLink= ...
说明
@ObjectLink装饰的变量不能被赋值,如果要使用赋值操作,请使用@Prop。
- @Prop装饰的变量和数据源的关系是是单向同步,@Prop装饰的变量在本地拷贝了数据源,所以它允许本地更改,如果父组件中的数据源有更新,@Prop装饰的变量本地的修改将被覆盖;
- @ObjectLink装饰的变量和数据源的关系是双向同步,@ObjectLink装饰的变量相当于指向数据源的指针。如果一旦发生@ObjectLink装饰的变量的赋值,则同步链将被打断。
变量的传递/访问规则说明

图1 初始化规则图示
观察变化和行为表现
观察的变化
@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.初始渲染:
a.@Observed装饰的class的实例会被不透明的代理对象包装,代理了class上的属性的setter和getter方法
b.子组件中@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() {// in low version,DevEco may throw a warning,but it does not matter.// you can still compile and run.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次更新:a.ForEach:数组项的赋值导致ForEach的itemGenerator被修改,因此数组项被识别为有更改,ForEach的item builder将执行,创建新的ViewA组件实例。
b.ViewA({ label:
ViewA this.arrA[last], a: this.arrA[this.arrA.length-1] }):上述更改改变了数组中第二个元素,所以绑定this.arrA[1]的ViewA将被更新; -
this.arrA.push(new ClassA(0)) : 将触发2次不同效果的更新:
a.ForEach:新添加的ClassA对象对于ForEach是未知的itemGenerator,ForEach的item builder将执行,创建新的ViewA组件实例。
b.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基础技能学习路线图

领取以上完整高清学习路线图,请点击→《鸿蒙开发学习之应用模型》小编自己整理的部分学习资料(包含有高清视频、开发文档、电子书籍等)
以上分享的学习路线都适合哪些人跟着学习?
- -应届生/计算机专业通过学习鸿蒙新兴技术,入行互联网,未来高起点就业。
- -0基础转行提前布局新方向,抓住风口,自我提升,获得更多就业机会。
- -技术提升/进阶跳槽发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术,享受蓝海红利。

最后
鸿蒙开发学习是一个系统化的过程,从基础知识的学习到实战技能的锤炼,再到对前沿技术的探索,每一环节都至关重要。希望这份教程资料能帮助您快速入门并在鸿蒙开发之路上步步攀升,成就一番事业。让我们一起乘风破浪,拥抱鸿蒙生态的广阔未来!
如果你觉得这篇内容对你有帮助,我想麻烦大家动动小手给我:点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
关注我,同时可以期待后续文章ing,不定期分享原创知识。
想要获取更多完整鸿蒙最新VIP学习资料,请点击→《鸿蒙基础入门学习指南》
相关文章:
HarmonyOS—@Observed装饰器和@ObjectLink嵌套类对象属性变化
Observed装饰器和ObjectLink装饰器:嵌套类对象属性变化 概述 ObjectLink和Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步: 被Observed装饰的类,可以被观察到属性的变化;子组件中ObjectLink装饰器装饰的状…...
The method toList() is undefined for the type Stream
The method toList() is undefined for the type Stream (JDK16) default List<T> toList() { return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray()))); }...
vue+element (el-progress)标签 隐藏百分比(%) ,反向显示 ,自定义颜色, demo 复制粘贴拿去用
1 效果: 2 页面代码: <el-row :gutter"10" ><el-col :span"12"><el-card ><div class"fourqu"><div><span slot"title">{{推送任务TOP5}}</span></div></div><div class&…...
Android轻量级进程间通信Messenger源码分析
一. 概述 Android中比较有代表性的两大通信机制:1. 线程间Handler通信 2. 进程间Binder通信,本篇文章中我们在理解AIDL原理的基础上来解读一下Messenger的源代码, 并结合示例Demo加深理解。 在看本篇文章前,建议先查阅一下笔者的…...
C#开发AGV地图编辑软件
C#自己开发AGV地图编辑软件: 1、自由添加和删除站点、停车位、小车、运行路径。 2、编辑得地图以XML文件保存。 3、导入编辑好地图的XML文件。 4、程序都是源码,可以直接在此基础上进行二次开发。 下载链接:https://download.csdn.net/d…...
嵌入式学习day22 Linux
文件IO: 1. lseek off_t lseek(int fd, off_t offset, int whence); 功能: 重新设定文件描述符的偏移量 参数: fd:文件描述符 offset:偏移量 whence: SEEK_SET 文件开头 …...
不确定性问题的论文笔记
Statistics starting from 01/2024, 仅列出了优秀工作中的一部分 每一年的排列顺序: CVPR, ICLR, ECCV, ICCV, ICML, AAAI, TPAMI,TIP,Arxiv 等 每周更新 2024 论文信息速览笔记是 否 已精读精读笔记Shao W, Xu Y, Peng L, et al. Failure Detection fo…...
C语言推荐书籍
本书详细讲解了C语言的基本概念和编程技巧。全书共17章。第1章、第2章介绍了C语言编程的预备知识。第3章~第15章详细讲解了C语言的相关知识,包括数据类型、格式化输入/输出、运算符、表达式、语句、循环、字符输入和输出、函数、数组和指针、字符和字符串…...
基于uniapp微信小程序的汽车租赁预约系统
随着现代汽车租赁管理的快速发展,可以说汽车租赁管理已经逐渐成为现代汽车租赁管理过程中最为重要的部分之一。但是一直以来我国传统的汽车租赁管理并没有建立一套完善的行之有效的汽车租赁管理系统,传统的汽车租赁管理已经无法适应高速发展,…...
ClickHouse 基础(一)
官网 ClickHouse release 24.1, 2024-01-30 以毫秒为单位查询数十亿行 ClickHouse是用于实时应用和分析的最快、资源效率最高的开源数据库。 安装ClickHouse 使用ClickHouse,你有三个选择: ClickHouse云:官方ClickHouse作为一项服务,-由ClickHouse的创…...
07-k8s中secret资源02-玩转secret
一、回顾secret资源的简单实用 第一步:将想要的数据信息【key:value】中的value值,使用base64编码后,写入secret资源清单中; 第二步:创建secret资源; 第三步:pod资源引用secret资源&…...
HTTP特性
大家好我是苏麟 , 今天说说HTTP特性. 资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 到目前为止,HTTP 常见到版本有 HTTP/1.1,HTTP/2.0,HTTP/3.0,不同版本的 HTTP 特性是不一样的。 这里先用 HTTP/1.1 版本给大家介…...
ARM 之十六 详解 CMSIS 版本变迁、各组件使用示例
目前,CMSIS 已经发展到了第六版,其目录结构也发生了重大的变化。在不断发展中,很多原来 CMSIS 的组件被不断独立出去,并因此成立了很多开源社区,今天就来学习一下! 由于 CMSIS 已经包含了相当丰富的文档,因此,本文重点学习版本之间的变化以及一些实际使用示例。 什么是…...
【北京游戏业:出海竞争实力全面】
本文将深入分析北京的游戏行业发展。在上海、广州、北京、深圳、成都、杭州、福建七大游戏产业中心城市中,北京无疑是出海竞争力最强的游戏产业集群。本文将全面剖析北京游戏行业的发展现状。 北京是中国游戏产业的发源地。拥有从游戏引擎到美术设计等完整的产业链…...
课程大纲:图像处理中的矩阵计算
课程名称:《图像处理中的矩阵计算》 课程简介: 图像处理中的矩阵计算是图像分析与处理的核心部分。本课程旨在教授学员如何应用线性代数中的矩阵计算,以实现各种图像处理技术。我们将通过强调实际应用和实践活动来确保学员能够理解和掌握这些…...
【Go语言】Go语言的数据类型
GO 语言的数据类型 Go 语言内置对以下这些基本数据类型的支持: 布尔类型:bool 整型:int8、byte、int16、int、uint、uintptr 等 浮点类型:float32、float64 复数类型:complex64、complex128 字符串:st…...
2024年2月19日 - mis
当需要在两个或多个子模式间插入文本时,这个特性尤其有用。这里有个脚本,它使用子模式在大数字中插入逗号。 $ echo "1234567" | sed { > :start > s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/ > t start > } 1,234,567 $ 这个脚本将匹…...
【JavaWeb】网上蛋糕商城-项目搭建
学习目标 了解网上蛋糕商城的项目需求 了解网上蛋糕商城的功能结构 熟悉E-R图和数据表的设计 熟悉项目环境的搭建 通过前面章节的学习,相信读者应该已经掌握了Web开发的基础知识,学习这些基础知识就是为开发Web网站奠定基础。如今,电子商…...
【Flink状态管理五】Checkpoint的设计与实现
文章目录 1. Checkpoint的整体设计2. Checkpoint创建源码解析2.1. DefaultExecutionGraphBuilder.buildGraph2.2. ExecutionGraph.enableCheckpointing 由于系统原因导致Flink作业无法正常运行的情况非常多,且很多时候都是无法避免的。对于Flink集群来讲,…...
How to install a specific version of a package in R
How to install a specific version of a package in R 在使用R语言完成数据分析的过程中,很多时候,因为项目实际需要,我们应该指定某些库文件的安装包的版本,这个时候,我们可以基于devtools包中的函数install_version…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器
从本章节开始,进入到函数有多个参数的情况,前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参,ECX是整型的第一个参数的寄存器,那么多个参数的情况下函数如何传参,下面展开介绍参数为整型时候的几种情…...
【芯片仿真中的X值:隐藏的陷阱与应对之道】
在芯片设计的世界里,X值(不定态)就像一个潜伏的幽灵。它可能让仿真测试顺利通过,却在芯片流片后引发灾难性后果。本文将揭开X值的本质,探讨其危害,并分享高效调试与预防的实战经验。 一、X值的本质与致…...
React 样式方案与状态方案初探
React 本身只提供了基础 UI 层开发范式,其他特性的支持需要借助相关社区方案实现。本文将介绍 React 应用体系中样式方案与状态方案的主流选择,帮助开发者根据项目需求做出合适的选择。 1. React 样式方案 1.1. 内联样式 (Inline Styles) 通过 style …...
智能问数Text2SQL Vanna windows场景验证
架构 Vanna 是一个开源 Python RAG(检索增强生成)框架,用于 SQL 生成和相关功能。 机制 Vanna 的工作过程分为两个简单步骤 - 在您的数据上训练 RAG“模型”,然后提出问题,这些问题将返回 SQL 查询,这些查…...
