OpenHarmony嵌套類對象屬性變化:@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可能會帶來問題。

裝飾器說明

@ObjectLink裝飾的數據為可讀示例。

// 允許@ObjectLink裝飾的數據屬性賦值
this.objLink.a= ...
// 不允許@ObjectLink裝飾的數據自身賦值
this.objLink= ...

說明:

@ObjectLink裝飾的變量不能被賦值,如果要使用賦值操作,請使用 @Prop 。

●?@Prop裝飾的變量和數據源的關系是是單向同步,@Prop裝飾的變量在本地拷貝了數據源,所以它允許本地更改,如果父組件中的數據源有更新,@Prop裝飾的變量本地的修改將被覆蓋;

●?@ObjectLink裝飾的變量和數據源的關系是雙向同步,@ObjectLink裝飾的變量相當于指向數據源的指針。禁止對@ObjectLink裝飾的變量賦值,如果一旦發生@ObjectLink裝飾的變量的賦值,則同步鏈將被打斷。因為@ObjectLink修飾的變量通過數據源(Object)引用來初始化。對于實現雙向數據同步的@ObjectLink,賦值相當于更新父組件中的數組項或者class的屬性,TypeScript/JavaScript不能實現,會發生運行時報錯。

變量的傳遞/訪問規則說明

圖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的屬性的變化,示例請參考 對象數組 。

繼承Date的class時,可以觀察到Date整體的賦值,同時可通過調用Date的接口setFullYear,?setMonth,?setDate,?setHours,?setMinutes,?setSeconds,?setMilliseconds,?setTime,?setUTCFullYear,?setUTCMonth,?setUTCDate,?setUTCHours,?setUTCMinutes,?setUTCSeconds,?setUTCMilliseconds?更新Date的屬性。

@Observed
class DateClass extends Date {constructor(args: number | string) {super(args)}
}@Observed
class ClassB {public a: DateClass;constructor(a: DateClass) {this.a = a;}
}@Component
struct ViewA {label: string = 'date';@ObjectLink a: DateClass;build() {Column() {Button(`child increase the day by 1`).onClick(() => {this.a.setDate(this.a.getDate() + 1);})DatePicker({start: new Date('1970-1-1'),end: new Date('2100-1-1'),selected: this.a})}}
}@Entry
@Component
struct ViewB {@State b: ClassB = new ClassB(new DateClass('2023-1-1'));build() {Column() {ViewA({ label: 'date', a: this.b.a })Button(`parent update the new date`).onClick(() => {this.b.a = new DateClass('2023-07-07');})Button(`ViewB: this.b = new ClassB(new DateClass('2023-08-20'))`).onClick(() => {this.b = new ClassB(new DateClass('2023-08-20'));})}}
}

框架行為

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;}
}@Observed
class ClassD {public c: ClassC;constructor(c: ClassC) {this.c = c;}
}@Observed
class ClassC extends ClassA {public k: number;constructor(k: number) {// 調用父類方法對k進行處理super(k);this.k = k;}
}

以下組件層次結構呈現的是嵌套類對象的數據結構。

@Component
struct ViewC {label: string = 'ViewC1';@ObjectLink c: ClassC;build() {Row() {Column() {Text(`ViewC [${this.label}] this.a.c = ${this.c.c}`).fontColor('#ffffffff').backgroundColor('#ff3fc4c4').height(50).borderRadius(25)Button(`ViewC: this.c.c add 1`).backgroundColor('#ff7fcf58').onClick(() => {this.c.c += 1;console.log('this.c.c:' + this.c.c)})}.width(300)}
}
}@Entry
@Component
struct ViewB {@State b: ClassB = new ClassB(new ClassA(0));@State child : ClassD = new ClassD(new ClassC(0));build() {Column() {ViewC({ label: 'ViewC #3', c: this.child.c})Button(`ViewC: this.child.c.c add 10`).backgroundColor('#ff7fcf58').onClick(() => {this.child.c.c += 10console.log('this.child.c.c:' + this.child.c.c)})}}
}

被@Observed裝飾的ClassC類,可以觀測到繼承基類的屬性的變化。

ViewB中的事件句柄:

●?this.child.c?=?new?ClassA(0)?和this.b?=?new?ClassB(new?ClassA(0)):?對@State裝飾的變量b和其屬性的修改。

●?this.child.c.c?=?…?:該變化屬于第二層的變化, @State 無法觀察到第二層的變化,但是ClassA被@Observed裝飾,ClassA的屬性c的變化可以被@ObjectLink觀察到。

ViewC中的事件句柄:

●?this.c.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: ClassA) => {ViewA({ label: `#${item.id}`, a: item })},(item: ClassA): string => 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次更新:

○?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的item?builder將執行,創建新的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) 無法觀察到第二層的變化,但是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: string | Resource) => {Text(item).width(100).height(100)},(item: string) => 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: StringArray) => {ItemPage({ itemArr: itemArr })},(itemArr: string) => itemArr[0])Divider()Button('update').onClick(() => {console.error('Update all items in arr');if ((this.arr[0] as Array<String>)[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('!');}})}}
}

如果大家想更加深入的學習 OpenHarmony 開發的內容,不妨可以參考以下相關學習文檔進行學習,助你快速提升自己:

OpenHarmony 開發環境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源碼解析》:https://qr18.cn/CgxrRy

  • 搭建開發環境
  • Windows 開發環境的搭建
  • Ubuntu 開發環境搭建
  • Linux 與 Windows 之間的文件共享
  • ……

系統架構分析:https://qr18.cn/CgxrRy

  • 構建子系統
  • 啟動流程
  • 子系統
  • 分布式任務調度子系統
  • 分布式通信子系統
  • 驅動子系統
  • ……

OpenHarmony 設備開發學習手冊:https://qr18.cn/CgxrRy

在這里插入圖片描述

OpenHarmony面試題(內含參考答案):https://qr18.cn/CgxrRy

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/bicheng/21891.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/21891.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/21891.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

實時瞳孔分割算法-RITnet論文復現

源代碼在這里RITnet-Github 這個模型比較小眾&#xff0c;我們實驗室使用了官方提供的模型進行瞳孔中心位置提取&#xff0c;以實現視線追蹤&#xff0c;效果很好 一、數據集準備 RITnet也是那一屆openEDS數據集挑戰賽的冠軍模型&#xff0c;openEDS數據集可以從Kaggle上下載&a…

數據結構--關鍵路徑

事件v1-表示整個工程開始&#xff08;源點&#xff1a;入度為0的頂點&#xff09; 事件v9-表示整個工程結束&#xff08;匯點&#xff1a;出度為0的頂點&#xff09; 關鍵路徑&#xff1a;路徑長度最長的路徑 求解關鍵路徑問題&#xff08;AOE網&#xff09; 定義四個描述量 …

網絡網絡層之(6)ICMPv4協議

網絡網絡層之(6)ICMPv4協議 Author: Once Day Date: 2024年6月2日 一位熱衷于Linux學習和開發的菜鳥&#xff0c;試圖譜寫一場冒險之旅&#xff0c;也許終點只是一場白日夢… 漫漫長路&#xff0c;有人對你微笑過嘛… 全系列文章可參考專欄: 通信網絡技術_Once-Day的博客-CS…

華為OD刷題C卷 - 每日刷題 7(字符串分隔(二)、歡樂的周末)

兩段代碼分別解決了兩個不同的問題&#xff0c;下面是對它們的概述&#xff1a; 1、&#xff08;字符串分隔&#xff08;二&#xff09;&#xff09;&#xff1a; 這段代碼是解決“字符串分隔&#xff08;二&#xff09;”的問題。它提供了一個Java類Main&#xff0c;其中包含…

Java面試題-Tomcat高級面試題

Tomcat 9相較于之前的版本有哪些主要的新特性&#xff1f; Tomcat 9相較于之前的版本&#xff0c;主要有以下幾個新特性&#xff1a; HTTP/2支持&#xff1a;Tomcat 9引入了對HTTP/2協議的支持&#xff0c;這可以顯著提高網站的性能和安全性。HTTP/2協議允許單個連接上進行多…

多語言for循環遍歷總結

多語言for循環遍歷總結 工作中經常需要遍歷對象&#xff0c;但不同編程語言之間存在一些細微差別。為了便于比較和參考&#xff0c;這里對一些常用的遍歷方法進行了總結。 JAVA 數組遍歷 Test void ArrayForTest() {String[] array {"劉備","關羽", &…

大模型備案項目補貼政策一覽【保持更新】

大模型項目、AI類項目、大模型備案通過后等一籃子財政補貼政策 上海市 加快創新體系構建 1. 提升自主創新水平&#xff1a;對引領大模型發展或取得顛覆性突破的項目&#xff0c;最高給予1000萬元補貼支持。 2. 加強算力資源保障&#xff1a;實施算力伙伴計劃&#xff0c;對…

力扣174題動態規劃:地下城游戲(含模擬面試)

?????? 歡迎來到我的博客。希望您能在這里找到既有價值又有趣的內容&#xff0c;和我一起探索、學習和成長。歡迎評論區暢所欲言、享受知識的樂趣&#xff01; 推薦&#xff1a;數據分析螺絲釘的首頁 關注微信公眾號 數據分析螺絲釘 免費領取價值萬元的python/java/商業…

Java進階學習筆記36——算法

什么是算法&#xff1f; 解決某個實際問題的過程和方法。 1&#xff09;導航&#xff1b; 2&#xff09;滴滴打車&#xff1b; 3&#xff09;抖音&#xff1b; 不同的算法&#xff0c;效率高、性能好&#xff01; 在Java中&#xff0c;代碼已經幫我們寫好了&#xff0c;但為…

雪花算法詳解及源碼分析

雪花算法的簡介&#xff1a; 雪花算法用來實現全局唯一ID的業務主鍵&#xff0c;解決分庫分表之后主鍵的唯一性問題&#xff0c;所以就單從全局唯一性來說&#xff0c;其實有很多的解決方法&#xff0c;比如說UUID、數據庫的全局表的自增ID 但是在實際的開發過程中&#xff0…

離散點云擬合三維平面參數推導(基于最小二乘)

1、背景介紹 實際中&#xff0c;很多人工構造物是由平面結構構造而成&#xff0c;如下圖所示&#xff0c;為一典型的由多個平面組成的人工構筑物。因此&#xff0c;根據離散點擬合成平面&#xff0c;獲取擬合平面方程&#xff0c;是點云數據處理中非常常見的數據處理操作。 2、…

鴻蒙Ability Kit(程序框架服務)【ExtensionAbility組件】

ExtensionAbility組件 ExtensionAbility組件是基于特定場景&#xff08;例如服務卡片、輸入法等&#xff09;提供的應用組件&#xff0c;以便滿足更多的使用場景。 每一個具體場景對應一個[ExtensionAbilityType]&#xff0c;開發者只能使用&#xff08;包括實現和訪問&#…

WPS的excel表格設置了編輯權限,要怎么取消?

在日常生活和工作中&#xff0c;我們經常會使用WPS Office辦公軟件來處理各種文檔&#xff0c;其中WPS Excel表格是我們進行數據處理和分析的重要工具。為了保護表格中的數據不被隨意修改&#xff0c;我們有時會設置編輯權限。然而&#xff0c;隨著時間的推移或需求的變更&…

基于FPGA的SystemVerilog練習

文章目錄 一、認識SystemVerilogSystemVerilog的語言特性SystemVerilog的應用領域SystemVerilog的優勢SystemVerilog的未來發展方向 二、流水燈代碼流水燈部分testbench仿真文件 三、用systemVerilog實現超聲波測距計時器測距部分led部分數碼管部分采樣部分頂層文件引腳綁定效果…

魯教版七年級數學下冊-筆記

文章目錄 第七章 二元一次方程組1 二元一次方程組2 解二元一次方程組3 二元一次方程組的應用4 二元一次方程與一次函數5 三元一次方程組 第八章 平行線的有關證明1 定義與命題2 證明的必要性3 基本事實與定理4 平行線的判定定理5 平行限的性質定理6 三角形內角和定理 第九章 概…

dpdk uio整體分析及網卡加載

參考:https://zhuanlan.zhihu.com/p/477600165 一、Linux內核知識點 1. __attribute__ constructor/destructor (1)若函數被設定為constructor屬性,則該函數會在 main()函數執行之前被自動的執行。 (2)若函數被設定為destructor屬性,則該函數會在main()函數執…

開發和滲透偷懶利器utools

目錄 1.前言 1.1 工具簡介 1.2 核心特性 1.3 使用場景 1.4 安裝與使用 1.4.1 下載&#xff1a; 1.4.2 安裝&#xff1a; 1.4.3 配置&#xff1a; 1.4.4 插件市場&#xff1a; 2.懶狗插件介紹 基本介紹 2.1 數據模擬 2.2 隨機生成虛假數據 2.3 API市場 2.4 Hoppscot…

【十二】圖解mybatis日志模塊之設計模式

圖解mybatis日志模塊之設計模式 概述 最近經常在思考研發工程師初、中、高級工程師以及系統架構師各個級別的工程師有什么區別&#xff0c;隨著年齡增加我們的技術級別也在提升&#xff0c;但是很多人到了高級別反而更加憂慮&#xff0c;因為it行業35歲年齡是個坎這是行業里的共…

一文讀懂數據庫中的DB、DBMS、DBS、DBAS

目前數據庫的應用非常廣泛,幾乎各行各業都在直接或間接地與數據庫打交道,例如網上購物、銀行業務、鐵路購票和酒店住宿等。在實際應用中,數據庫、數據庫管理系統、數據庫系統和數據庫應用系統經常被統稱為數據庫,而實質上這4個概念是不一樣的,它們具有不同的定義和含義。下…

暴力數據結構之排序大雜燴

1. 冒泡排序&#xff1a;O(N^2) 邏輯解析&#xff1a; 冒泡排序并沒有什么實際意義&#xff0c;但是有教學意義&#xff0c;相信大部分小白在學習的初期第一個接觸的排序就是冒泡排序。那么接下來我們了解一下他的底層邏輯&#xff1a; 冒泡排序顧名思義就是將最大&#xff08…