15、鴻蒙Harmony Next開發:創建自定義組件

目錄

自定義組件的基本用法

自定義組件的基本結構

struct

@Component

freezeWhenInactive

?build()函數

@Entry

EntryOptions

@Reusable

成員函數/變量

自定義組件的參數規定

build()函數

自定義組件生命周期

自定義組件的創建和渲染流程

自定義組件重新渲染

自定義組件的刪除

自定義組件嵌套使用與示例


在ArkUI中,UI顯示的內容均為組件,由框架直接提供的稱為系統組件,由開發者定義的稱為自定義組件。進行UI界面開發時,不僅要組合使用系統組件,還需考慮代碼的可復用性、業務邏輯與UI的分離,以及后續版本的演進等因素。因此,將UI和部分業務邏輯封裝成自定義組件是不可或缺的能力。

自定義組件的特點:

  1. 可組合:允許開發者組合使用系統組件、及其屬性和方法。
  2. 可重用:自定義組件可以被其他組件重用,并作為不同的實例在不同的父組件或容器中使用。
  3. 數據驅動UI更新:通過狀態變量的改變,來驅動UI的刷新。

自定義組件的基本用法

@Component
struct HelloComponent {@State message: string = 'Hello, World!';build() {// HelloComponent自定義組件組合系統組件Row和TextRow() {Text(this.message).onClick(() => {// 狀態變量message的改變驅動UI刷新,UI從'Hello, World!'刷新為'Hello, ArkUI!'this.message = 'Hello, ArkUI!';})}}
}

注意

如果在其他文件中引用自定義組件,需要使用export關鍵字導出組件,并在使用的頁面import該自定義組件。

?可以在其他自定義組件的build()函數中多次創建HelloComponent,以實現自定義組件的重用。

@Entry
@Component
struct ParentComponent {build() {Column() {Text('ArkUI message')HelloComponent({ message: 'Hello World!' });Divider()HelloComponent({ message: '你好,世界!' });}}
}

自定義組件的基本結構

struct

自定義組件基于struct實現,struct + 自定義組件名 + {...}的組合構成自定義組件,不能有繼承關系。對于struct的實例化,可以省略new。

說明

自定義組件名、類名、函數名不得與系統組件名重復。

@Component

@Component裝飾器僅能裝飾struct關鍵字聲明的數據結構。struct被@Component裝飾后具備組件化的能力,需要實現build方法描述UI,一個struct只能被一個@Component裝飾。@Component可以接受一個可選的boolean類型參數。

說明

從API version 9開始,該裝飾器支持在ArkTS卡片中使用。

從API version 11開始,@Component可以接受一個可選的boolean類型參數。

@Component
struct MyComponent {
}

freezeWhenInactive

組件凍結選項。

自定義組件凍結功能專為優化復雜UI頁面的性能而設計,尤其適用于包含多個頁面棧、長列表或宮格布局的場景。當狀態變量綁定多個UI組件時,其變化易觸發大量組件刷新,導致界面卡頓與響應延遲。為提升這類高負載UI界面的刷新性能,建議開發者使用自定義組件凍結功能。

組件凍結功能是一種性能優化機制,它會凍結非激活狀態下的組件的刷新能力。當組件處于非激活狀態時,即使其綁定的狀態變量發生變化,也不會觸發該組件的UI重新渲染,從而降低復雜UI場景下的刷新負載。

組件凍結的工作原理是:

  1. 開發者通過設置freezeWhenInactive屬性,即可激活組件凍結機制。
  2. 啟用后,系統將僅對處于激活狀態的自定義組件進行更新,這使得UI框架可以盡量縮小更新范圍,僅限于用戶可見范圍內(激活狀態)的自定義組件,從而提高復雜UI場景下的刷新效率。
  3. 當之前處于inactive狀態的自定義組件重新變為active狀態時,狀態管理框架會對其執行必要的刷新操作,確保UI的正確展示。

需要注意,組件active/inactive并不等同于其可見性。組件凍結目前僅適用于以下場景:

  1. 頁面路由:當前棧頂頁面為active狀態,非棧頂不可見頁面為inactive狀態。
  2. TabContent:只有當前顯示的TabContent中的自定義組件處于active狀態,其余則為inactive。
  3. LazyForEach:僅當前顯示的LazyForEach中的自定義組件為active狀態,而緩存節點的組件則為inactive狀態。
  4. Navigation:當前顯示的NavDestination中的自定義組件為active狀態,而其他未顯示的NavDestination組件則為inactive狀態。
  5. 組件復用:進入復用池的組件為inactive狀態,從復用池上樹的節點為active狀態。
  6. 混用場景:對于以上場景的組合使用,例如TabContent下面使用LazyForEach,切換Tab時,API version 17及以下,LazyForEach中的所有節點都會被設置為active狀態,而從API version 18開始,只有LazyForEach的屏上節點會被設置為active狀態,其余則為inactive狀態。
名稱類型必填說明
freezeWhenInactiveboolean是否開啟組件凍結。默認值false。true:開啟組件凍結,false:不開啟組件凍結。
@Component({ freezeWhenInactive: true })
struct MyComponent {
}

?build()函數

build()函數用于定義自定義組件的聲明式UI描述,自定義組件必須定義build()函數。

@Component
struct MyComponent {build() {}
}

@Entry

@Entry裝飾的自定義組件將作為UI頁面的入口。在單個UI頁面中,僅允許存在一個由@Entry裝飾的自定義組件作為頁面的入口。@Entry可以接受一個可選的LocalStorage的參數。

說明

從API version 9開始,該裝飾器支持在ArkTS卡片中使用。

從API version 10開始,@Entry可以接受一個可選的LocalStorage的參數或者一個可選的EntryOptions參數。

從API version 11開始,該裝飾器支持在元服務中使用。

@Entry
@Component
struct MyComponent {
}

EntryOptions

命名路由跳轉選項。

名稱類型必填說明
routeNamestring表示作為命名路由頁面的名字。
storageLocalStorage頁面級的UI狀態存儲。
useSharedStorageboolean是否使用LocalStorage.getShared()接口返回的共享的LocalStorage實例對象。默認值false。true:使用共享的LocalStorage實例對象。false:不使用共享的LocalStorage實例對象。

說明

當useSharedStorage設置為true,并且storage也被賦值時,useSharedStorage的值優先級更高。

@Entry({ routeName : 'myPage' })
@Component
struct MyComponent {
}

@Reusable

@Reusable裝飾的自定義組件具備可復用能力。詳細請參考:@Reusable裝飾器:組件復用。

說明

從API version 10開始,該裝飾器支持在ArkTS卡片中使用。

@Reusable
@Component
struct MyComponent {
}

成員函數/變量

自定義組件除了必須要實現build()函數外,還可以實現其他成員函數,成員函數具有以下約束:

  • 自定義組件的成員函數為私有的,且不建議聲明為靜態函數。

自定義組件可以包含成員變量,成員變量具有以下約束:

  • 自定義組件的成員變量為私有的,且不建議聲明成靜態變量。
  • 自定義組件的成員變量本地初始化有些是可選的,有些是必選的。

自定義組件的參數規定

以上示例中,可以在build方法里創建自定義組件,同時在創建自定義組件的過程中,根據裝飾器的規則來初始化自定義組件的參數。

@Component
struct MyComponent {private countDownFrom: number = 0;private color: Color = Color.Blue;build() {}
}@Entry
@Component
struct ParentComponent {private someColor: Color = Color.Pink;build() {Column() {// 創建MyComponent實例,并將創建MyComponent成員變量countDownFrom初始化為10,將成員變量color初始化為this.someColorMyComponent({ countDownFrom: 10, color: this.someColor })}}
}

以下示例代碼將父組件中的函數傳遞給子組件,并在子組件中調用。

@Entry
@Component
struct Parent {@State cnt: number = 0submit: () => void = () => {this.cnt++;}build() {Column() {Text(`${this.cnt}`)Son({ submitArrow: this.submit })}}
}@Component
struct Son {submitArrow?: () => voidbuild() {Row() {Button('add').width(80).onClick(() => {if (this.submitArrow) {this.submitArrow()}})}.height(56)}
}

build()函數

所有在build()函數中聲明的語句統稱為UI描述,UI描述需要遵循以下規則:

  • @Entry裝飾的自定義組件,其build()函數下的根節點唯一且必要,且必須為容器組件,其中ForEach禁止作為根節點
  • @Component裝飾的自定義組件,其build()函數下的根節點唯一且必要,可以為非容器組件,其中ForEach禁止作為根節點。
    @Entry
    @Component
    struct MyComponent {build() {// 根節點唯一且必要,必須為容器組件Row() {ChildComponent() }}
    }@Component
    struct ChildComponent {build() {// 根節點唯一且必要,可為非容器組件Image('test.jpg')}
    }
    
  • 不允許聲明本地變量,反例如下。
    build() {// 反例:不允許聲明本地變量let num: number = 1;
    }
    
  • 不允許在UI描述里直接使用console.info,但允許在方法或者函數里使用,反例如下。
    build() {// 反例:不允許console.infoconsole.info('print debug log');
    }
    
  • 不允許創建本地的作用域,反例如下。
    build() {// 反例:不允許本地作用域{// ...}
    }
    
  • ?不允許調用沒有用@Builder裝飾的方法,允許系統組件的參數是TS方法的返回值。
    @Component
    struct ParentComponent {doSomeCalculations() {}calcTextValue(): string {return 'Hello World';}@Builder doSomeRender() {Text(`Hello World`)}build() {Column() {// 反例:不能調用沒有用@Builder裝飾的方法this.doSomeCalculations();// 正例:可以調用this.doSomeRender();// 正例:參數可以為調用TS方法的返回值Text(this.calcTextValue())}}
    }
    
  • 不允許使用switch語法,當需要使用條件判斷時,請使用if。示例如下。
    build() {Column() {// 反例:不允許使用switch語法switch (expression) {case 1:Text('...')break;case 2:Image('...')break;default:Text('...')break;}// 正例:使用ifif(expression == 1) {Text('...')} else if(expression == 2) {Image('...')} else {Text('...')}}
    }
    
  • 不允許使用表達式,請使用if組件,示例如下。
    build() {Column() {// 反例:不允許使用表達式(this.aVar > 10) ? Text('...') : Image('...')// 正例:使用if判斷if(this.aVar > 10) {Text('...')} else {Image('...')}}
    }
    
  • 不允許直接改變狀態變量,反例如下。
    @Component
    struct MyComponent {@State textColor: Color = Color.Yellow;@State columnColor: Color = Color.Green;@State count: number = 1;build() {Column() {// 應避免直接在Text組件內改變count的值Text(`${this.count++}`).width(50).height(50).fontColor(this.textColor).onClick(() => {this.columnColor = Color.Red;})Button("change textColor").onClick(() =>{this.textColor = Color.Pink;})}.backgroundColor(this.columnColor)}
    }
    

    在ArkUI狀態管理中,狀態驅動UI更新。

自定義組件生命周期

自定義組件生命周期,即用@Component或@ComponentV2裝飾的自定義組件的生命周期,提供以下生命周期接口:

  • aboutToAppear:組件即將出現時回調該接口,具體時機為在創建自定義組件的新實例后,在執行其build函數之前執行。
  • onDidBuild:在組件首次渲染觸發的build函數執行完成之后回調該接口,后續組件重新渲染將不回調該接口。開發者可以在這個階段進行埋點數據上報等不影響實際UI的功能。不建議在onDidBuild函數中更改狀態變量、使用animateTo等功能,這可能會導致不穩定的UI表現。
  • aboutToDisappear:aboutToDisappear函數在自定義組件析構銷毀之前執行。不允許在aboutToDisappear函數中改變狀態變量,特別是@Link變量的修改可能會導致應用程序行為不穩定。

自定義組件生命周期流程如下圖所示。

根據上面的流程圖,接下來從自定義組件的初始創建、重新渲染和刪除來詳細說明。

自定義組件的創建和渲染流程

  1. 自定義組件的創建:自定義組件的實例由ArkUI框架創建。
  2. 初始化自定義組件的成員變量:通過本地默認值或者構造方法傳遞參數來初始化自定義組件的成員變量,初始化順序為成員變量的定義順序。
  3. 如果開發者定義了aboutToAppear,則執行該方法。
  4. 在首次渲染的時候,執行build方法渲染系統組件,如果子組件為自定義組件,則創建自定義組件的實例。在首次渲染的過程中,框架會記錄狀態變量和組件的映射關系,當狀態變量改變時,驅動其相關的組件刷新。
  5. 如果開發者定義了onDidBuild,則執行該方法。

自定義組件重新渲染

當觸發事件(比如點擊)改變狀態變量時,或者LocalStorage / AppStorage中的屬性更改,并導致綁定的狀態變量更改其值時:

  1. 框架觀察到變化,啟動重新渲染。
  2. 根據框架記錄的狀態變量和組件的映射關系,僅刷新發生變化的狀態變量所關聯的組件,實現最小化更新。

自定義組件的刪除

例如if組件的分支改變或ForEach循環渲染中數組的個數改變,組件將被移除:

  1. 在刪除組件之前,將調用其aboutToDisappear生命周期函數,標記著該節點將要被銷毀。ArkUI的節點刪除機制是:后端節點直接從組件樹上摘下,后端節點被銷毀,對前端節點解引用,前端節點已經沒有引用時,將被Ark虛擬機垃圾回收。
  2. 自定義組件和它的變量將被刪除,如果組件有同步的變量(如@Link、@Prop、@StorageLink),將從同步源上取消注冊。

不建議在生命周期aboutToDisappear中使用async await。如果在此生命周期中使用異步操作(如 Promise 或回調方法),自定義組件將被保留在Promise的閉包中,直到回調方法執行完畢,這會阻止自定義組件的垃圾回收。

自定義組件嵌套使用與示例

通過以下示例,來詳細說明自定義組件在嵌套使用時,自定義組件生命周期的調用時序:

// Index.ets
@Entry
@Component
struct Parent {@State showChild: boolean = true;@State btnColor: string = "#FF007DFF";// 組件生命周期aboutToAppear() {console.info('Parent aboutToAppear');}// 組件生命周期onDidBuild() {console.info('Parent onDidBuild');}// 組件生命周期aboutToDisappear() {console.info('Parent aboutToDisappear');}build() {Column() {// this.showChild為true,創建Child子組件,執行Child aboutToAppearif (this.showChild) {Child()}Button('delete Child').margin(20).backgroundColor(this.btnColor).onClick(() => {// 更改this.showChild為false,刪除Child子組件,執行Child aboutToDisappear// 更改this.showChild為true,添加Child子組件,執行Child aboutToAppearthis.showChild = !this.showChild;})}}
}@Component
struct Child {@State title: string = 'Hello World';// 組件生命周期aboutToDisappear() {console.info('Child aboutToDisappear');}// 組件生命周期onDidBuild() {console.info('Child onDidBuild');}// 組件生命周期aboutToAppear() {console.info('Child aboutToAppear');}build() {Text(this.title).fontSize(50).margin(20).onClick(() => {this.title = 'Hello ArkUI';})}
}

以上示例中,Index頁面包含兩個自定義組件,一個是Parent,一個是Child,Parent及其子組件Child分別聲明了各自的自定義組件生命周期函數(aboutToAppear / onDidBuild / aboutToDisappear)。

  • 應用冷啟動的初始化流程為:Parent aboutToAppear --> Parent build --> Parent onDidBuild --> Child aboutToAppear --> Child build --> Child onDidBuild。此處體現了自定義組件懶展開特性,即Parent執行完onDidBuild之后才會執行Child組件的aboutToAppear。日志輸出信息如下:

Parent aboutToAppear

Parent onDidBuild
Child aboutToAppear
Child onDidBuild

  • 點擊Button按鈕,更改showChild為false,刪除Child組件,執行Child aboutToDisappear方法。
  • 如果直接退出應用,則會觸發以下生命周期:Parent aboutToDisappear --> Child aboutToDisappear,此處體現了自定義組件刪除順序也是從父到子。日志輸出信息如下:

Parent aboutToDisappear

Child aboutToDisappear

  • 最小化應用或者應用進入后臺,當前Index頁面未被銷毀,所以并不會執行組件的aboutToDisappear。
  • 如果showChild的默認值為false,則應用冷啟動的初始化流程為:Parent aboutToAppear --> Parent build --> Parent onDidBuild。日志輸出信息如下:

Parent aboutToAppear
Parent onDidBuild

  • 如果showChild的默認值為false,直接退出應用,則只執行Parent aboutToDisappear方法。
  • 如果showChild的默認值為false,此時點擊Button按鈕,更改showChild為true,添加Child組件,添加流程為:Child aboutToAppear --> Child build --> Child onDidBuild。日志輸出信息如下:

Child aboutToAppear
Child onDidBuild

當showchild為默認值true時,該示例的生命周期流程圖如下所示:

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

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

相關文章

深入理解Map.Entry.comparingByValue()和Map.Entry.comparingByKey()

文章目錄深入理解Map.Entry.comparingByValue()和Map.Entry.comparingByKey()1. 方法定義comparingByKey()comparingByValue()2. 基本用法2.1 使用comparingByKey()2.2 使用comparingByValue()3. 方法重載版本comparingByKey(Comparator)comparingByValue(Comparator)4. 高級用…

Mac下載mysql

安裝 brew list --versions | grep mysql查看已安裝的mysql版本brew search mysql查看支持的mysql版本brew info mysql查看mysql版本信息brew install mysql進行安裝/opt/homebrew/opt/mysql/bin/mysqld --initialize-insecure --user$(whoami) --basedir$(brew --prefix mysql…

PageHelper使用說明文檔

文章目錄一、簡介二、集成步驟三、使用方法四、注意事項五、高級用法一、簡介 PageHelper 是一個開源的 MyBatis 分頁插件,它可以幫助我們在使用 MyBatis 進行數據庫操作時方便地實現分頁功能。通過簡單的配置和少量的代碼修改,就可以在查詢數據時實現分…

grpo nl2sql qwen3 模型強化學習訓練有效果的成立條件有哪些

在使用GRPO(強化學習算法)對Qwen3模型在NL2SQL(自然語言到SQL轉換)任務上進行強化學習(RL)訓練時,其效果成立的核心條件可歸納為以下幾個關鍵維度,這些條件相互關聯,共同…

面向向量檢索的教育QA建模:九段日本文化研究所日本語學院的Prompt策略分析(6 / 500)

面向向量檢索的教育QA建模:九段日本文化研究所日本語學院的Prompt策略分析(6 / 500) 系列說明 500 所日本語言學校結構化建模實戰,第 6 篇。每篇拆解 1 所學校在 Prompt-QA 系統中的建模策略,分享工程經驗,…

墨刀原型圖的原理、與UI設計圖的區別及轉換方法詳解-卓伊凡|貝貝

墨刀原型圖的原理、與UI設計圖的區別及轉換方法詳解-卓伊凡|貝貝最近有個設計由于時間比較倉促直接用 原型做的,但是原型做的大家都知道是沒法用的,以下講解原型和ui的區別,其次我們下面有三種方法把墨刀的原型變成UI圖。一、墨刀原型圖的原理…

前端 nodejs vue2 開發環境和微信開發環境 故障終極處理

現象某個vue2舊項目 引入vue-ls 組件等組件,沖突失敗后刪除,導致開發環境 vxe-table加載失敗,還原后還是不行。前段項目崩潰。報警sass 某個方法 Deprecated ,之前不會處理方式_失敗回退代碼項目代碼 刪除 node_modules, 刪除 …

【后端】.NET Core API框架搭建(9) --配置使用Log4Net日志

目錄 1.添加包 2.新建公用類 3.新建配置 4.注冊 4.1.類庫項目設置 5.使用 在 .NET Core 項目中使用 Log4Net 做日志記錄,具有很多優勢。盡管 .NET Core 自帶了 ILogger 接口(如使用內置的 ConsoleLogger、DebugLogger 等),但…

Agent交互細節

本文參考了https://www.bilibili.com/video/BV1v9V5zSEHA/視頻及原作者代碼實踐 本文主要實踐在第3節1、MCP MCP官方地址:https://modelcontextprotocol.io/introduction MCP 是一個開放協議,它規范了應用程序向 LLM 提供上下文的方式。 架構&#xff1a…

AI+醫療!VR和MR解剖學和針灸平臺,智能時代如何重塑健康未來

在智能時代,“AI醫療”正從精準診斷入手,推動醫療系統變革,通過個性化健康管理、智能診療輔助等方式重塑健康未來!將人工智能(AI)與虛擬實境(VR)應用到中醫教學,透過該系…

Sersync和Rsync部署

學習參考連接 以下是我在學習過程中借鑒的經驗和下載資源鏈接,感謝幾位大佬的幫助,也供各位參考。 Rsync踩坑: https://blog.csdn.net/XiaoXiaoYunXing/article/details/120160395 Sersync下載源 http://down.whsir.com/downloads/sersy…

Django基礎(四)———模板常用過濾器

前言上篇文章給大家介紹了DTL模板的部分知識點這篇文章繼續帶大家深入理解Django框架中的模板過濾器一、模板常用過濾器1.add將傳進來的參數添加到原來的值上面。這個過濾器會嘗試將值和 參數轉換成整形然后進行相加。如果轉換成整形過程中失敗了,那么會將值和參數進…

國內MCP服務器搜索引擎有哪些?MCP導航站平臺推薦

在人工智能技術蓬勃發展的今天&#xff0c;AI模型與外部工具和服務的交互能力正成為推動技術進步的關鍵。AIbase&#xff08;<https://mcp.aibase.cn/>&#xff09;作為一個專注于MCP(Model Context Protocol&#xff0c;模型上下文協議)服務器的集合平臺&#xff0c;為全…

Python中with的作用和用法

在這里我們來詳細解釋一下Python中非常重要的 with 語句。 我會從 “為什么需要它” 開始&#xff0c;然后講解 “它是什么以及如何使用”&#xff0c;最后深入到 “它的工作原理” 和 “如何自定義”。1. 為什么需要 with 語句&#xff1f;(The Problem) 在編程中&#xff0c;…

緩存雪崩、緩存穿透,緩存擊穿

Redis是一個完全開源免費的高性能非關系型&#xff08;NOSQL&#xff09;的key-value數據庫。 Redis不可能把所有的數據都緩存起來(內存昂貴且有限)&#xff0c;所以Redis需要對數據 設置過期時間&#xff0c;并采用的是惰性刪除定期刪除兩種策略對過期鍵刪除。Redis對過期鍵的…

springmvc跨域解決方案

在Spring MVC中處理跨域請求&#xff08;CORS&#xff0c;Cross-Origin Resource Sharing&#xff09;通常涉及到配置HTTP響應頭&#xff0c;以允許來自不同源的請求。Spring MVC提供了多種方式來配置CORS&#xff0c;包括全局配置和局部配置。 使用CrossOrigin注解 在控制器的…

btstack移植之安全配對(二)

3.13.3 Legacy配對首先&#xff0c;我們回復的paring response中&#xff0c;可以看到我們不支持secure connection&#xff0c;所以我們走的是legacy配對模式。圖3-74 secure連接不支持然后&#xff0c;master在pairing confirm包中回復了confirm value。圖3-75 master發送con…

FRP配置( CentOS 7 上安裝 FRP教程 )

** 如果你們公司沒有公網IP&#xff0c;但是又想實現內網穿透&#xff0c;遠程調用接口&#xff0c;在家也能調用公司服務器&#xff0c;但是nkg ssl有問題&#xff0c;花生殼坑壁&#xff0c;那么FRP是你最佳的選擇&#xff01;&#xff01;&#xff01;** 不過有個前提&#…

第三次mysql作業

建立庫建立mydb11_syu庫2.創建s表&#xff0c;創建sc表二&#xff0e;插入數據向s表插入數據2.向sc表插入數據三&#xff0e;查詢1.分別查詢student表和score表的所有記錄2.查詢student表的第2條到5條記錄3.從student表中查詢計算機系和英語系的學生的信息4.從student表中查詢年…

不同場景下git指令的搭配

添加賬號 git config --global user.name "YourName" git config --global user.email "your_emailexample.com"設置 Git 默認分支名稱為 main&#xff1a; git config --global init.defaultBranch main初始化倉庫&#xff1a; git init配置SSH 密鑰 如果…