一、概述
在鴻蒙Next中,@Observed和@ObjectLink裝飾器用于處理嵌套類對象屬性變化,實(shí)現(xiàn)雙向數(shù)據(jù)同步,彌補(bǔ)了其他裝飾器只能觀察一層變化的局限。從API version 9開始,這兩個(gè)裝飾器支持在ArkTS卡片中使用,從API version 11開始,支持在元服務(wù)中使用。
(一)功能特性
-
@Observed用于標(biāo)記類,使其創(chuàng)建的實(shí)例能被觀察到屬性變化,主要用于嵌套類場景。 -
@ObjectLink裝飾子組件中的狀態(tài)變量,接收@Observed裝飾的類的實(shí)例,與父組件中對應(yīng)的狀態(tài)變量建立雙向數(shù)據(jù)綁定。
二、裝飾器說明
(一)@Observed類裝飾器
- 參數(shù):無。
-
類裝飾器:用于修飾class,需放在class定義前,通過
new創(chuàng)建類對象。
(二)@ObjectLink變量裝飾器
- 參數(shù):無。
-
允許裝飾的變量類型
- 必須為被
@Observed裝飾的class實(shí)例,且必須指定類型。 - 不支持簡單類型,若需使用簡單類型可考慮
@Prop。 - 支持繼承Date、Array的class實(shí)例,API11及以上支持繼承Map、Set的class實(shí)例,API11及以上還支持
@Observed裝飾類和undefined或null組成的聯(lián)合類型。 - 變量屬性可改變,但變量自身分配不允許,即該裝飾器裝飾的變量只讀,不能被重新賦值(整體替換除外,但要在父組件進(jìn)行)。
- 必須為被
三、變量的傳遞/訪問規(guī)則
-
從父組件初始化
- 必須指定。
- 初始化
@ObjectLink裝飾的變量需同時(shí)滿足以下條件:- 類型必須是
@Observed裝飾的class。 - 初始化數(shù)值需是數(shù)組項(xiàng)或class的屬性。
- 同步源的class或數(shù)組必須是
@State、@Link、@Provide、@Consume或者@ObjectLink裝飾的數(shù)據(jù)。
- 類型必須是
- 與源對象同步:雙向同步。
-
可以初始化子組件:允許,可用于初始化常規(guī)變量、
@State、@Link、@Prop、@Provide。
四、觀察變化和行為表現(xiàn)
(一)觀察變化
-
@Observed裝飾的類- 若屬性為非簡單類型(如class、Object或數(shù)組),也需被
@Observed裝飾,否則其屬性變化無法觀察到。
- 若屬性為非簡單類型(如class、Object或數(shù)組),也需被
-
@ObjectLink裝飾的變量- 可觀察到屬性數(shù)值的變化(屬性指Object.keys返回的所有屬性)。
- 若數(shù)據(jù)源是數(shù)組,可觀察到數(shù)組item的替換;若數(shù)據(jù)源是class,可觀察到class的屬性變化。
- 對于繼承Date、Map、Set的class實(shí)例,可分別觀察到相應(yīng)類型的整體賦值及通過接口更新值的操作(如Date的日期屬性更新、Map的鍵值更新、Set的元素更新等)。
(二)框架行為
-
初始渲染
-
@Observed裝飾的class的實(shí)例被不透明代理對象包裝,代理其屬性的setter和getter方法。 - 子組件中
@ObjectLink裝飾的變量從父組件初始化,接收@Observed裝飾的class的實(shí)例,@ObjectLink的包裝類將自己注冊給@Observed class。
-
-
屬性更新
- 當(dāng)
@Observed裝飾的class屬性改變時(shí),執(zhí)行代理的setter和getter,遍歷依賴它的@ObjectLink包裝類,通知數(shù)據(jù)更新。
- 當(dāng)
五、使用場景示例
(一)嵌套對象
- 定義了
Bag、User、Book、BookName等類,均被@Observed裝飾,形成嵌套對象結(jié)構(gòu)。 - 在
ViewA、ViewC子組件中通過@ObjectLink接收父組件ViewB中@State變量的屬性(如Bag實(shí)例或BookName實(shí)例),實(shí)現(xiàn)雙向同步。 - 在
ViewB中對user.bag、child.bookName.size等屬性的修改,能在相關(guān)子組件中同步更新。注意@ObjectLink變量只讀,不能進(jìn)行整體賦值操作(如this.bookName = new bookName(...)),但可更改成員屬性(如this.bookName.size += 1)。
(二)對象數(shù)組
- 定義
Info類被@Observed裝飾,Parent組件中有@State裝飾的Info[]數(shù)組。 -
Child子組件通過@ObjectLink接收Info實(shí)例,實(shí)現(xiàn)雙向同步。 - 在
Parent中對數(shù)組的操作(如push、shift、修改數(shù)組項(xiàng)屬性等)會(huì)觸發(fā)不同的更新效果,涉及ForEach的重新執(zhí)行和相關(guān)子組件的刷新,而對于@State無法觀察到的第二層屬性變化(如Info類中的info屬性),由于Info被@Observed裝飾,可被@ObjectLink觀察到并同步更新。
(三)二維數(shù)組
- 聲明
StringArray類繼承Array<string>并被@Observed裝飾,通過new創(chuàng)建實(shí)例。 - 在
IndexPage組件中定義@State裝飾的Array<StringArray>二維數(shù)組,ItemPage子組件通過@ObjectLink接收StringArray實(shí)例,實(shí)現(xiàn)雙向同步。 - 在
IndexPage中對二維數(shù)組元素的操作(如push)會(huì)觸發(fā)相應(yīng)的UI更新。
(四)繼承Map類(API11及以上)
- 定義
Info類包含MyMap<number, string>類型的屬性,MyMap類需被@Observed裝飾(此處假設(shè)已定義)。 - 在相關(guān)組件中,通過
@ObjectLink實(shí)現(xiàn)對MyMap類型數(shù)據(jù)的雙向同步,點(diǎn)擊按鈕改變MyMap屬性時(shí),視圖會(huì)隨之刷新。同理,對于繼承Set類也類似(可參考繼承Set類部分的示例)。
六、限制條件
- 使用
@Observed裝飾class會(huì)改變其原始原型鏈,與其他類裝飾器一起使用可能導(dǎo)致問題。 -
@ObjectLink裝飾器不能在@Entry裝飾的自定義組件中使用。 -
@ObjectLink裝飾的變量類型需為顯式的被@Observed裝飾的類,未指定類型或類型錯(cuò)誤在編譯期會(huì)報(bào)錯(cuò)。 -
@ObjectLink裝飾的變量不能本地初始化,只能通過構(gòu)造參數(shù)從父組件傳入初始值,否則編譯報(bào)錯(cuò)。且變量只讀,賦值操作會(huì)導(dǎo)致運(yùn)行時(shí)報(bào)錯(cuò)(除在父組件進(jìn)行整體替換)。
七、常見問題及注意事項(xiàng)
(一)在子組件中給@ObjectLink裝飾的變量賦值
不允許直接賦值,會(huì)導(dǎo)致同步鏈打斷和運(yùn)行時(shí)報(bào)錯(cuò),應(yīng)在父組件進(jìn)行整體替換或僅修改其屬性。
(二)基礎(chǔ)嵌套對象屬性更改失效
確保嵌套對象中的類及其屬性(非簡單類型)都被@Observed裝飾,否則屬性變化無法被觀察到。
(三)復(fù)雜嵌套對象屬性更改失效
如二維數(shù)組等復(fù)雜嵌套結(jié)構(gòu),同樣要保證各層級相關(guān)類都被@Observed裝飾,以實(shí)現(xiàn)屬性變化的觀察和同步。
(四)@Prop與@ObjectLink的差異
@Prop裝飾的變量與數(shù)據(jù)源單向同步,允許本地更改但父組件數(shù)據(jù)源更新時(shí)本地修改會(huì)被覆蓋;@ObjectLink裝飾的變量與數(shù)據(jù)源雙向同步,相當(dāng)于指向數(shù)據(jù)源的指針,只讀且不能重新賦值(整體替換在父組件進(jìn)行)。
(五)在@Observed裝飾類的構(gòu)造函數(shù)中延時(shí)更改成員變量
不建議在構(gòu)造函數(shù)中進(jìn)行延時(shí)更改成員變量的操作,可能導(dǎo)致預(yù)期外的行為,應(yīng)在合適的時(shí)機(jī)修改屬性以確保變化可被觀察和同步。
(六)@ObjectLink數(shù)據(jù)源更新時(shí)機(jī)
數(shù)據(jù)源(@Observed裝飾的類實(shí)例)的屬性變化會(huì)觸發(fā)@ObjectLink裝飾變量的更新,從而更新相關(guān)UI組件,但要注意同步機(jī)制和觸發(fā)條件,如ForEach的更新機(jī)制對數(shù)組操作的影響等。
(七)使用a.b(this.object)形式調(diào)用,不會(huì)觸發(fā)UI刷新
當(dāng)@ObjectLink裝飾的變量是Object類型,且在build方法內(nèi)通過a.b(this.object)形式調(diào)用(如靜態(tài)方法或組件內(nèi)部方法修改Object屬性)時(shí),無法觸發(fā)UI刷新。解決方法是先對變量進(jìn)行賦值,使修改操作作用于帶有Proxy代理的變量,從而實(shí)現(xiàn)UI刷新。