鴻蒙HarmonyOS NEXT開發:優化用戶界面性能——組件復用(@Reusable裝飾器)

文章目錄

      • 一、概述
      • 二、原理介紹
      • 三、使用規則
      • 四、復用類型詳解
        • 1、標準型
        • 2、有限變化型
          • 2.1、類型1和類型2布局不同,業務邏輯不同
          • 2.2、類型1和類型2布局不同,但是很多業務邏輯公用
        • 3、組合型
        • 4、全局型
        • 5、嵌套型

一、概述

組件復用是優化用戶界面性能,提升應用流暢度的一種重要手段,通過復用已存在的組件節點而非創建新的節點,從而確保UI線程的流暢性與響應速度。

組件復用針對的是自定義組件,只要發生了相同自定義組件銷毀和再創建的場景,都可以使用組件復用,例如滑動列表場景,會出現大量重復布局的創建,使用組件復用可以大幅度降低了因頻繁創建與銷毀組件帶來的性能損耗。

然而,面對復雜的業務場景或者布局嵌套的場景下,組件復用使用不當,可能會導致復用失效或者性能提升不能最大化。例如列表中存在多種布局形態的列表項,無法直接復用。

本文基于對常見的布局類型進行劃分,通過合理使用組件復用方式,幫助開發者更好的理解和實施組件復用策略以優化應用性能。

二、原理介紹

組件復用機制如下:

  • 標記為@Reusable的組件從組件樹上被移除時,組件和其對應的JSView對象都會被放入復用緩存中。
  • 當列表滑動新的ListItem將要被顯示,List組件樹上需要新建節點時,將會從復用緩存中查找可復用的組件節點。
  • 找到可復用節點并對其進行更新后添加到組件樹中。從而節省了組件節點和JSView對象的創建時間。

組件復用原理圖

在這里插入圖片描述

1、@Reusable表示組件可以被復用,結合LazyForEach懶加載一起使用,可以進一步解決列表滑動場景的瓶頸問題,提供滑動場景下高性能創建組件的方式來提升滑動幀率。

2、CustomNode是一種自定義的虛擬節點,它可以用來緩存列表中的某些內容,以提高性能和減少不必要的渲染。通過使用CustomNode,可以實現只渲染當前可見區域內的數據項,將未顯示的數據項緩存起來,從而減少渲染的數量,提高性能。

3、RecycleManager是一種用于優化資源利用的回收管理器。當一個數據項滾出屏幕時,不會立即銷毀對應的視圖對象,而是將該視圖對象放入復用池中。當新的數據項需要在屏幕上展示時,RecycleManager會從復用池中取出一個已經存在的視圖對象,并將新的數據綁定到該視圖上,從而避免頻繁的創建和銷毀過程。通過使用RecycleManager,可以大大減少創建和銷毀視圖的次數,提高列表的滾動流暢度和性能表現。

4、CachedRecycleNodes是CustomNode的一個集合,常是用于存儲被回收的CustomNode對象,以便在需要時進行復用。

說明
需要注意的是,雖然這里是使用List組件進行舉例,但是不代表組件復用只能用在滾動容器里,只要是發生了相同自定義組件銷毀和再創建的場景,都可以使用組件復用。

三、使用規則

組件復用的示例代碼如下:

// xxx.ets
export class Message {value: string | undefined;constructor(value: string) {this.value = value}
}@Entry
@Component
struct Index {@State switch: boolean = truebuild() {Column() {Button('Hello World').fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {this.switch = !this.switch})if (this.switch) {Child({ message: new Message('Child') })// 如果只有一個復用的組件,可以不用設置reuseId.reuseId('Child')}}.height("100%").width('100%')}
}@Reusable
@Component
struct Child {@State message: Message = new Message('AboutToReuse');aboutToReuse(params: Record<string, ESObject>) {console.info("Recycle Child")this.message = params.message as Message}build() {Column() {Text(this.message.value).fontSize(20)}.borderWidth(2).height(100)}
}

1.@Reusable:自定義組件被@Reusable裝飾器修飾,即表示其具備組件復用的能力。

2.aboutToReuse:當一個可復用的自定義組件從復用緩存中重新加入到節點樹時,觸發aboutToReuse生命周期回調,并將組件的構造參數傳遞給aboutToReuse。

3.reuseId:用于標記自定義組件復用組,當組件回收復用時,復用框架將根據組件的reuseId來劃分組件的復用組。如果只有一個復用的組件,可以不用設置reuseId。

四、復用類型詳解

組件復用基于不同的布局效果和復用的訴求,可以分為以下五種類型。

表1 組件復用類型說明

復用類型描述復用思路
標準型復用組件之間布局完全相同標準復用
有限變化型復用組件之間布局有所不同,但是類型有限使用reuseId或者獨立成不同自定義組件
組合型復用組件之間布局有不同,情況非常多,但是擁有共同的子組件將復用組件改為@Builder,讓內部子組件相互之間復用
全局型組件可在不同的父組件中復用,并且不適合使用@Builder使用BuilderNode自定義復用組件池,在整個應用中自由流轉
嵌套型復用組件的子組件的子組件存在差異采用化歸思想將嵌套問題轉化為上面四種標準類型來解決

下面將以滑動列表的場景為例介紹5種復用類型的使用場景,為了方便描述,下文將需要復用的自定義組件如ListItem的內容組件,叫做復用組件,將其下層的自定義組件叫做子組件、復用組件上層的自定義組件叫做父組件。為了更直觀,下面每一種復用類型都會通過簡易的圖形展示組件的布局方式,并且為了便于分辨,布局相同的子組件使用同一種形狀圖形表示。

1、標準型

在這里插入圖片描述

這是一個標準的組件復用場景,一個滾動容器內的復用組件布局相同,只有數據不同,這種類型的組件復用可以直接參考資料組件復用。其緩存池如下,因為該場景只有一個復用組件,所以在緩存中只有一個復用組件list:

在這里插入圖片描述

典型場景如下,列表Item布局基本完全相同。

在這里插入圖片描述

標準型組件復用的示例代碼如下:

@Entry
@Component
struct ReuseType1 {// ...build() {Column() {List() {LazyForEach(this.dataSource, (item: string) => {ListItem() {CardView({ item: item })}}, (item: string) => item)}}}
}// 復用組件
@Reusable
@Component
export struct CardView {@State item: string = '';aboutToReuse(params: Record<string, Object>): void {this.item = params.item as string;}// ...
}
2、有限變化型

在這里插入圖片描述

如上圖所示,有限變化型指的是父組件內存在多個類型的復用單元,這些類型的單元布局有所不同,根據業務邏輯的差異可以分為以下兩種情況:

  • 類型1和類型2布局不同,業務邏輯不同:這種情況可以使用兩個不同的自定義組件進行復用。

  • 類型1和類型2布局不同,但是很多業務邏輯公用:這種情況為了復用公用的邏輯代碼,減少代碼冗余,可以給同一個組件設置不同的reuseId來進行復用。

下面將分別介紹這兩種場景下的組件復用方法。

2.1、類型1和類型2布局不同,業務邏輯不同

在這里插入圖片描述

類型1和類型2布局不同,業務邏輯不同:因為兩種類型的組件布局會對應應用不同的業務處理邏輯,建議將兩種類型的組件分別使用兩個不同的自定義組件,分別進行復用。給復用組件1和復用組件2設置不同的reuseId,此時組件復用池內的狀態如下圖所示,復用組件1和復用組件2處于不同的復用list中。

例如下面的列表場景,列表項布局差距比較大,有多圖片的列表項,有單圖片的列表項:

在這里插入圖片描述

實現方式可參考以下示例代碼:

@Entry
@Component
struct ReuseType2A {// ...build() {Column() {List() {LazyForEach(this.dataSource, (item: number) => {ListItem() {if (item % 2 === 0) { // 模擬業務條件判斷SinglePicture({ item: item }) // 渲染單圖片列表項} else {MultiPicture({ item: item }) // 渲染多圖片列表項}}}, (item: number) => item + '')}}}
}// 復用組件1
@Reusable
@Component
struct SinglePicture {// ...
}// 復用組件2
@Reusable
@Component
struct MultiPicture {// ...
}
2.2、類型1和類型2布局不同,但是很多業務邏輯公用

在這里插入圖片描述

類型1和類型2布局不同,但是很多業務邏輯公用:在這種情況下,如果將組件分為兩個自定義組件進行復用,會存在代碼冗余問題。根據布局的差異,可以給同一個組件設置不同的reuseId從而復用同一個組件,達到邏輯代碼的復用。

根據組件復用原理與使用可知,復用組件是依據reuseId來區分復用緩存池的,而自定義組件的名稱就是默認的reuseId。因此,為復用組件顯式設置兩個不同的reuseId與使用兩個自定義組件進行復用,對于 ArkUI 而言,復用邏輯完全相同,復用池也一樣,只不過復用池中復用組件的list以reuseId作為標識。

例如下面這個場景,布局差異比較小,業務邏輯一樣都是跳轉到頁面詳情。這種情況復用同一個組件,只需要使用if/else條件語句來控制布局的結構,就可以實現,同時可以復用跳轉詳情的公用邏輯代碼。但是這樣會導致在不同邏輯會反復去修改布局,造成性能損耗。開發者可以根據不同的條件,設置不同的reuseId來標識需要復用的組件,省去重復執行if的刪除重創邏輯,提高組件復用的效率和性能。

在這里插入圖片描述

實現方式可以參考以下示例:

@Entry
@Component
struct ReuseType2B {// ...build() {Column() {List() {LazyForEach(this.dataSource, (item: MemoInfo) => {ListItem() {MemoItem({ memoItem: item })// 使用reuseId進行組件復用的控制.reuseId((item.imageSrc !== '') ? 'withImage' : 'noImage')}}, (item: MemoInfo) => JSON.stringify(item))}}}
}@Reusable
@Component
export default struct MemoItem {@State memoItem: MemoInfo = MEMO_DATA[0];aboutToReuse(params: Record<string, Object>) {this.memoItem = params.memoItem as MemoInfo;}build() {Row() {// ...if (this.memoItem.imageSrc !== '') {Image($r(this.memoItem.imageSrc)).width(90).aspectRatio(1).borderRadius(10)}}// ...}
}
3、組合型

在這里插入圖片描述

這種類型中復用組件之間存在不同,并且情況比較多,但擁有共同的子組件。如果使用有限變化型的組件復用方式,將所有類型的復用組件寫成自定義組件分別復用,不同復用組件(組件名不同或者reuseld不同)之間相同子組件無法復用,因為它們在緩存池的不同List中。

對此可以將復用組件轉變為@Builder函數,使復用組件內部共同的子組件的緩存池在父組件上共享,此時組件復用池內的狀態如下圖所示。

典型場景如下圖,這個列表的Item有多種組合方式。但是每個Item上面和下面的布局是一樣的,中間部分的布局有所不同,有單一圖片、視頻、九宮等等。

在這里插入圖片描述

示例代碼如下,列舉了單一圖片、視頻和九宮格圖片三種類型的列表項目,使用Builder函數后將子組件組合成三種不同的類型,使內部共同的子組件就處于同一個父組件FriendsMomentsPage下。對這些子組件使用組件復用時,他們的緩存池也會在父組件上共享,節省組件創建時的消耗。

@Entry
@Component
struct ReuseType3 {// ...@BuilderitemBuilderSingleImage(item: FriendMoment) { // 單大圖列表項// ...}@BuilderitemBuilderGrid(item: FriendMoment) { // 九宮格列表項// ...}@BuilderitemBuilderVideo(item: FriendMoment) { // 視頻列表項// ...}build() {Column() {List() {LazyForEach(this.momentDataSource, (item: FriendMoment) => {ListItem() {if (item.type === 1) { // 根據不同類型,使用不同的組合this.itemBuilderSingleImage(item);} else if (item.type === 2) {this.itemBuilderGrid(item);} else if (item.type === 3) {this.itemBuilderVideo(item);} else {// ...}}}, (moment: FriendMoment) => JSON.stringify(moment))}}}
}@Reusable
@Component
struct ItemTop {// ...
}@Reusable
@Component
struct ItemBottom {// ...
}@Reusable
@Component
struct MiddleSingleImage {// ...
}@Reusable
@Component
struct MiddleGrid {// ...
}@Reusable
@Component
struct MiddleVideo {// ...
}
4、全局型

在這里插入圖片描述

默認的組件復用行為,是將子組件放在父組件的緩存池里,受到這個限制,不同父組件中的相同子組件無法復用,推薦的解決方案是將父組件改為builder函數,讓子組件共享組件復用池,但是由于在一些應用場景下,父組件承載了復雜的帶狀態的業務邏輯,而builder是無狀態的,修改會導致難以維護,因此開發者可以使用BuilderNode自行管理組件復用池。

有時候應用在多個tab頁之間切換,tab頁之間結構類似,需要在tab頁之間復用組件,提升頁面切換性能。或者有些應用在組合型場景下,由于復用組件內部含有較多帶狀態的業務邏輯,所以不適合改為Builder函數。

針對這種類型的組件復用場景,可以通過BuilderNode自定義緩存池,將要復用的組件封裝在BuilderNode中,將BuilderNode的NodeController作為復用的最小單元,自行管理復用池。

5、嵌套型

在這里插入圖片描述

嵌套型是指復用組件的子組件的子組件之間存在差異的復用場景。如上圖所示,列表項復用組件1之間的差異是子組件B的子組件不一樣,有子組件C、D、E三種。這種情況可以運行化歸的思想,將復雜的問題轉化為已知的、簡單的問題

嵌套型實際上是上面四種類型的組合,以上圖為例,可以通過有限變化型的方案,將子組件B變為子組件B1/B2/B3,這樣問題就變成了一個標準的有限變化型,A/B1/C、A/B2/D、A/B3/E會分別作為一個組合進行復用,復用池如下:
在這里插入圖片描述

下面列舉一個簡單的示例介紹嵌套型的使用:

@Entry
@Component
struct ReuseType5A {// ...build() {Column() {List() {LazyForEach(this.dataSource, (item: number) => {ListItem() {if (item % 2 === 0) { // 模擬類型一的條件ReusableComponent({ item: item }).reuseId('type1')} else if (item % 3 === 0) { // 模擬類型二的條件ReusableComponent({ item: item }).reuseId('type2')} else { // 模擬類型三的條件ReusableComponent({ item: item }).reuseId('type3')}}}, (item: number) => item.toString())}}}
}// 復用組件
@Reusable
@Component
struct ReusableComponent {@State item: number = 0;build() {Column() {ComponentA()if (this.item % 2 === 0) {ComponentB1()} else if (this.item % 3 === 0) {ComponentB2()} else {ComponentB3()}}}
}@Component
struct ComponentA {// ...
}@Component
struct ComponentB1 {build() {Column() {ComponentC()}}
}@Component
struct ComponentB2 {build() {Column() {ComponentD()}}
}@Component
struct ComponentB3 {build() {Column() {ComponentE()}}
}@Component
struct ComponentC {// ...
}@Component
struct ComponentD {// ...
}@Component
struct ComponentE {// ...
}

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

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

相關文章

【AI大模型】Ollama部署本地大模型DeepSeek-R1,交互界面Open-WebUI,RagFlow構建私有知識庫

文章目錄 DeepSeek介紹公司背景核心技術產品與服務應用場景優勢與特點訪問與體驗各個DeepSeek-R系列模型的硬件需求和適用場景 Ollama主要特點優勢應用場景安裝和使用配置環境變量總結 安裝open-webui下載和安裝docker desktop配置鏡像源安裝open-webui運行和使用 RagFlow介紹主…

更加通用的Hexo多端部署原理及實現,適用于各種系統之間

本文推薦在作者的個人博客網站閱讀&#xff1a;shenying.online 一、故事背景 故事發生在大學上學期間&#xff08;而不是寒假&#xff09;。上學期間&#xff0c;宿舍條件極其惡劣&#xff0c;半夜斷電、空間狹小。我們大學垃圾條件使用游戲本的種種弊端被無限放大&#xff1…

開源、免費項目管理工具比較:2025最新整理30款

好用的開源、免費版項目管理系統有&#xff1a;1.Redmine&#xff1b;2. Taiga&#xff1b;3. OpenProject&#xff1b; 4.ProjectLibre&#xff1b; 5.GanttProject&#xff1b; 6.Tuleap&#xff1b; 7.Trac&#xff1b;8. Phabricator&#xff1b; 9.Notion&#xff1b; 10.…

組織結構改革:激活企業活力的 “源頭活水”

難以適應市場變化、內部溝通與協作不暢、決策效率低下、運營成本增加、人才流失嚴重、員工士氣下降、戰略目標難以實現……企業如何根據市場環境變化和自身發展需求&#xff0c;靈活調整組織框架&#xff0c;賦能企業的持續健康發展&#xff1f; 某國有投資建設集團旗下的二級…

oracle中decode怎么轉換成pg

對于 PostgreSQL 中的 Oracle DECODE 函數&#xff0c;可以使用 CASE 表達式或聯合。CASE 表達式根據條件返回第一個匹配的結果&#xff0c;語法為&#xff1a;CASE WHEN 條件 THEN 結果 ELSE 結果 END。聯合通過 UNION ALL 操作符組合多個 SELECT 語句&#xff0c;返回一個包含…

Mac之JDK安裝

Mac之JDK安裝 一.安裝 jdk 打開終端輸入命令:java -version 查看是否已安裝 JDK Oracle 官方下載地址 根據自己Mac 系統安裝 查看 Mac 系統&#xff0c;打開中斷命令&#xff0c;輸入: uname -a Compressed Archive 是壓縮文檔&#xff0c;下載的是一個 .tar.gz 壓縮包 D…

【含文檔+PPT+源碼】基于Python的全國景區數據分析以及可視化實現

項目介紹 本課程演示的是一款基于Python的全國景區數據分析以及可視化實現&#xff0c;主要針對計算機相關專業的正在做畢設的學生與需要項目實戰練習的 Java 學習者。 包含&#xff1a;項目源碼、項目文檔、數據庫腳本、軟件工具等所有資料 帶你從零開始部署運行本套系統 該…

Unity中快速制作2D沙雕動畫:流程編

Unity中快速制作2D沙雕動畫&#xff08;搞笑/無厘頭風格&#xff09;&#xff0c;通過以下方案實現低成本、高成效的開發流程&#xff0c;結合夸張的動作、滑稽的物理效果和魔性音效&#xff1a; 1. 角色與素材設計 核心原則&#xff1a;丑萌即正義&#xff0c;越怪越好&#…

保存字典類型的文件用什么格式比較好

保存 Python 字典類型的數據時&#xff0c;有幾個常見的格式可以選擇&#xff0c;這些格式都具有良好的可讀性和提取內容的便利性。以下是幾種推薦的格式&#xff1a; JSON 格式&#xff1a; 優點&#xff1a;JSON 格式非常適合存儲和傳輸結構化數據&#xff0c;具有良好的跨平…

SQL注入之布爾和時間盲注,sqli-labs

實驗環境&#xff1a; sqli-labs&#xff0c;小皮面板搭建&#xff0c;edge瀏覽器 apache&#xff1a;2.4.39&#xff0c;MySQL&#xff1a;5.7 PHP&#xff1a;5.39 Python&#xff08;pycharm2023&#xff09;:3 less-8 布爾盲注&#xff1a; 1.我這里是采用最簡單的直接采…

基于SSM的農產品供銷小程序+LW示例參考

1.項目介紹 系統角色&#xff1a;管理員、農戶功能模塊&#xff1a;用戶管理、農戶管理、產品分類管理、農產品管理、咨詢管理、訂單管理、收藏管理、購物車、充值、下單等技術選型&#xff1a;SSM&#xff0c;Vue&#xff08;后端管理web&#xff09;&#xff0c;uniapp等測試…

移植BOA服務器到GEC2440開發板

所需軟件:boa-0.94.13.tar.tar(下載:http://www.boa.org/boa-0.94.13.tar.gz) 步驟: 設置好交叉編譯工具鏈。 1、解壓下載好的壓縮包(tar xzvf boa-0.94.13.tar.tar),并進入解壓后的目錄(cd boa-0.94.13),再進行如下操作: 先進入到src目錄(下面操作都是在該目錄下進行…

Go 語言里中的堆與棧

在 Go 語言里&#xff0c;堆和棧是內存管理的兩個重要概念&#xff0c;它們在多個方面存在明顯差異&#xff1a; 1. 內存分配與回收方式 棧 分配&#xff1a;Go 語言中&#xff0c;棧內存主要用于存儲函數的局部變量和調用信息。當一個函數被調用時&#xff0c;Go 會自動為其…

從360度全景照片到高質量3D場景:介紹SC-Omnigs 3D重建系統

在當今的數字化時代,3D重建技術正在迅速發展,并廣泛應用于文旅、空間智能和3D重建等領域。為了簡化360度全景相機拍攝數據的處理流程,提高3D場景重建的質量和效率,我們開發了一款專門處理360度全景相機數據的3D重建系統——SC-Omnigs。本文將詳細介紹這一系統的功能、特點及…

LayUi table 列寬 th和td不同步變化

原因 在td所在的table中&#xff0c;當table寬度小于頁面寬度&#xff0c;滾動條會消失&#xff0c;進而table會被加以下樣式 style"width: auto"解決 在layui.js中將以下語句 !p&&h?y.width(o.getContentWidth(l)):y.width("auto")替換成 !…

HARCT 2025 分論壇10:Intelligent Medical Robotics智能醫療機器人

會議名稱&#xff1a;機電液一體化與先進機器人控制技術國際會議 會議簡稱&#xff1a;HARCT 2025 大會時間&#xff1a;2025年3月28日-30日 大會地點&#xff1a;中國桂林 主辦單位&#xff1a;桂林航天工業學院、廣西大學、桂林電子科技大學、桂林理工大學 協辦單位&…

前端可以不用依賴后端實現導出大數據了

theme: channing-cyan hightlight: channing-cyan 前言 在我們公司表格數據導出都是前端去處理。一開始數據量不大&#xff0c;倒沒什么問題。但隨著數據量的加大&#xff0c;問題也逐漸暴露出來。 一天的數據量有一來萬條&#xff0c;導出一定時間范圍的數據&#xff0c;30…

Java 后端開發:如何提取請求頭中的 Cookie 和身份驗證 Token

在 Java 后端開發中&#xff0c;處理 HTTP 請求時&#xff0c;經常需要從請求頭中提取特定的信息&#xff0c;如 Cookie 和身份驗證 Token。這些信息對于實現用戶會話管理、身份驗證和授權等功能至關重要。本文將詳細介紹如何在 Java 后端接口中提取請求頭中的 Cookie 和 Token…

游戲引擎學習第99天

倉庫:https://gitee.com/mrxiao_com/2d_game_2 黑板&#xff1a;制作一些光場(Light Field) 當前的目標是為游戲添加光照系統&#xff0c;并已完成了法線映射&#xff08;normal maps&#xff09;的管道&#xff0c;但還沒有創建可以供這些正常映射采樣的光場。為了繼續推進&…

通過 Docker 安裝和部署 KeyDB v6.3.4 的詳細步驟

KeyDB 是一種高性能的開源內存數據庫&#xff0c;最初是基于 Redis 項目開發的&#xff0c;但在性能、特性和功能上進行了許多增強和改進。它兼容 Redis 的大部分命令和數據結構&#xff0c;因此可以作為 Redis 的替代品使用&#xff0c;尤其是在需要更高性能和多線程支持的場景…