ArkTS四種渲染控制能力

威哥爱编程發表於2024-11-25

大家好,我是 V 哥。ArkTS 是 OpenHarmony 框架的一部分,提供了宣告式 UI 渲染的能力。下面來對ArkTS中四種渲染控制能力: if/elseForEachLazyForEachContentSlot 詳細介紹一下:

1. if/else 渲染控制

if/else 是 ArkTS 提供的基本邏輯控制語法,用於 基於條件動態控制元件的渲染。它的核心用途是根據某些條件,在執行時決定渲染哪些元件,以及元件的結構或內容。以下是它的主要特點和用途的詳細介紹:

主要用途

  1. 動態顯示或隱藏元件

    • 根據變數的值,動態控制某些元件是否渲染。
    • 避免不必要的元件渲染,提高效能。
  2. 實現多種狀態的介面切換

    • 適合條件分支較少的場景。
    • 在介面上根據狀態顯示不同的佈局或資訊(如登入狀態、載入中狀態、錯誤提示等)。
  3. 響應使用者互動或資料變化

    • 基於使用者的操作動態更新介面。
    • 如點選按鈕後切換檢視,或資料載入完成後切換顯示內容。
  4. 個性化內容顯示

    • 根據使用者角色、許可權或其他業務邏輯,動態展示不同的元件或內容。

詳細講解

語法基礎

  • if/else 語法與普通程式語言一致,可以巢狀其他 ArkTS UI 元件。
  • if 用於條件成立時渲染的元件,else 用於條件不成立時的渲染。

示例場景與程式碼

場景 1:登入狀態控制

根據使用者是否登入,顯示歡迎資訊或登入提示。

@Entry
@Component
struct LoginExample {
    private isLoggedIn: boolean = false;

    build() {
        Column() {
            // 根據使用者登入狀態顯示不同的內容
            if (this.isLoggedIn) {
                Text("歡迎回來,使用者!").fontSize(20).padding(10)
            } else {
                Text("您尚未登入,請登入繼續操作").fontSize(16).padding(10)
                Button("登入") {
                    this.isLoggedIn = true; // 登入後更新狀態
                }.padding(5)
            }
        }
    }
}

場景 2:載入狀態切換

顯示載入中的動畫或載入完成後的內容。

@Entry
@Component
struct LoadingExample {
    private isLoading: boolean = true;

    build() {
        Column() {
            // 判斷當前是否為載入狀態
            if (this.isLoading) {
                Text("載入中,請稍候...").fontSize(18).padding(10)
            } else {
                Text("內容載入完成!").fontSize(18).padding(10)
            }

            // 模擬狀態切換按鈕
            Button("切換狀態") {
                this.isLoading = !this.isLoading; // 切換載入狀態
            }.padding(10)
        }
    }
}

場景 3:許可權顯示

根據使用者許可權等級展示不同的內容。

@Entry
@Component
struct PermissionExample {
    private userRole: string = "guest"; // 使用者角色:guest, user, admin

    build() {
        Column() {
            if (this.userRole === "guest") {
                Text("歡迎遊客,請註冊以享受更多功能。").fontSize(16).padding(10)
            } else if (this.userRole === "user") {
                Text("歡迎普通使用者,解鎖更多高階功能升級為管理員。").fontSize(16).padding(10)
            } else if (this.userRole === "admin") {
                Text("歡迎管理員,您擁有最高許可權!").fontSize(16).padding(10)
            }

            // 模擬許可權切換按鈕
            Row() {
                Button("設定為遊客") { this.userRole = "guest" }.padding(5)
                Button("設定為普通使用者") { this.userRole = "user" }.padding(5)
                Button("設定為管理員") { this.userRole = "admin" }.padding(5)
            }
        }
    }
}

if/else 使用時的注意事項

  1. 避免複雜巢狀

    • 巢狀層級過深會影響程式碼的可讀性,建議將複雜邏輯拆分成方法或子元件。
  2. 效能最佳化

    • if/else 不會渲染未滿足條件的元件,因此可以利用該特性控制渲染數量,避免浪費資源。
  3. 配合狀態管理

    • 可以結合 State@Observed 資料模型,實現更靈活的動態渲染。

if/else 是 ArkTS 中最基礎的控制語法,透過動態切換元件的渲染路徑,可以靈活應對各種場景下的介面需求,使開發者能夠快速實現邏輯清晰、效能優異的動態 UI。


2. ForEach 渲染控制

ForEach 是 ArkTS 提供的一種迭代語法,專用於遍歷固定或小規模的資料集合,並基於集合的每個元素動態生成 UI 元件。它在開發中扮演著重要角色,尤其是需要根據陣列或列表生成介面內容時。

主要用途

  1. 動態渲染元件列表

    • 根據資料集合的元素,生成對應的元件。
    • 適合中小規模的靜態或動態資料集合。
  2. 資料驅動的檢視更新

    • 資料集合發生改變(增刪改)時,ForEach 會自動更新渲染內容,保證檢視與資料同步。
  3. 可配置的動態檢視

    • 使用相同的元件模板,為不同的資料生成獨特的內容或樣式。
  4. 清晰的邏輯分離

    • 資料與檢視邏輯解耦,透過資料集合定義 UI,避免手動建立多個類似元件。

語法

基本語法

ForEach(array, (item) => {
    // 渲染邏輯
})

引數

  • array:要遍歷的陣列或集合。
  • (item):當前元素的引用,可用於元件內容的動態生成。

示例與場景講解

場景 1:簡單列表渲染

示例:展示水果名稱的簡單列表。

@Entry
@Component
struct SimpleListExample {
    private fruits: string[] = ["蘋果", "香蕉", "橘子", "葡萄"];

    build() {
        Column() {
            ForEach(this.fruits, (fruit) => {
                Text(fruit).fontSize(20).padding(5)
            })
        }
    }
}

解析

  • 遍歷 fruits 陣列,動態生成每個 Text 元件。
  • fruits 陣列發生變化時(增刪項),介面會自動更新。

場景 2:帶索引的列表渲染

示例:顯示帶序號的城市列表。

@Entry
@Component
struct IndexedListExample {
    private cities: string[] = ["北京", "上海", "廣州", "深圳"];

    build() {
        Column() {
            ForEach(this.cities, (city, index) => {
                Text(`${index + 1}. ${city}`).fontSize(18).padding(5)
            })
        }
    }
}

解析

  • 使用 index 顯示當前元素的序號。
  • 可以動態繫結索引值到 UI。

場景 3:複雜資料物件渲染

示例:渲染使用者資訊卡片。

interface User {
    name: string;
    age: number;
}

@Entry
@Component
struct UserListExample {
    private users: User[] = [
        { name: "張三", age: 25 },
        { name: "李四", age: 30 },
        { name: "王五", age: 28 }
    ];

    build() {
        Column() {
            ForEach(this.users, (user) => {
                Column() {
                    Text(`姓名:${user.name}`).fontSize(18)
                    Text(`年齡:${user.age}`).fontSize(16).padding(2)
                }.padding(10).border(Color.Gray, 1).margin(5)
            })
        }
    }
}

解析

  • 遍歷 users 陣列,每個元素生成一個使用者資訊卡片。
  • 透過 Column 巢狀展示每個使用者的屬性資訊。

場景 4:動態列表(增刪項)

示例:實現可以增刪專案的任務列表。

@Entry
@Component
struct DynamicListExample {
    private tasks: string[] = ["完成報告", "開會", "整理資料"];

    build() {
        Column() {
            // 渲染任務列表
            ForEach(this.tasks, (task, index) => {
                Row() {
                    Text(task).fontSize(18).padding(5)
                    Button("刪除") {
                        this.tasks.splice(index, 1); // 刪除對應任務
                    }.padding(5)
                }
            })

            // 新增新任務的輸入框和按鈕
            Row() {
                TextField({ placeholder: "輸入新任務" }).onChange((value) => {
                    this.newTask = value;
                }).width('70%')
                Button("新增任務") {
                    if (this.newTask.trim() !== "") {
                        this.tasks.push(this.newTask.trim());
                    }
                }.padding(5)
            }.margin(10)
        }
    }

    private newTask: string = ""; // 輸入的新任務內容
}

解析

  • 使用 ForEach 渲染 tasks 列表。
  • tasks 資料更新時,UI 會同步變更。
  • 提供輸入框和按鈕以動態新增或刪除任務。

注意事項

  1. 適用於小規模資料集合

    • ForEach 適合幾百條以內的資料,資料量特別大時建議使用 LazyForEach
  2. 保持資料唯一性

    • 遍歷的資料集合應儘量保持唯一標識(如索引或 ID),避免意外更新或檢視錯亂。
  3. 避免過深巢狀

    • 如果 ForEach 巢狀過多,可能導致程式碼不易維護,建議拆分為子元件。

透過 ForEach 渲染控制,開發者能夠快速生成基於資料的動態 UI,滿足各種場景下的靈活需求,從靜態內容到動態互動都可以輕鬆實現。

3. LazyForEach 渲染控制

LazyForEach 是 ArkTS 提供的一種高效迭代語法,專為 大規模、動態資料集合 設計,透過 延遲載入 的機制,按需渲染可見範圍內的元件,從而顯著提高效能。

主要用途

  1. 大規模資料的高效渲染

    • 當資料集合規模較大(如數千條以上),LazyForEach 能避免一次性載入所有元件,從而節省記憶體和渲染時間。
  2. 按需載入資料

    • 根據當前檢視的可見範圍,僅渲染需要顯示的內容,未進入檢視的部分不會佔用計算資源。
  3. 支援動態增刪資料

    • 當資料集合動態變化(增刪、更新)時,LazyForEach 會自動最佳化渲染流程,保持效能穩定。
  4. 流暢的使用者體驗

    • 在需要滾動檢視長列表(如聊天記錄、商品列表)時,延遲載入機制保證了操作的流暢性。

語法

先來看一下基本語法

LazyForEach(array, (item, index) => {
    // 渲染邏輯
})

引數

  • array:需要遍歷的大規模資料集合。
  • (item, index):當前資料元素及其索引,傳遞給渲染邏輯。

示例與場景講解

場景 1:大規模資料的列表渲染

示例:顯示 1,000 條使用者訊息記錄。

@Entry
@Component
struct LargeDataExample {
    private messages: string[] = Array.from({ length: 1000 }, (_, i) => `訊息 ${i + 1}`);

    build() {
        LazyForEach(this.messages, (message) => {
            Text(message).fontSize(18).padding(5)
        })
    }
}

解析

  • 資料集合 messages 包含 1,000 條訊息。
  • LazyForEach 會根據可見範圍按需渲染訊息內容,減少一次性渲染的負擔,保證效能流暢。

場景 2:動態載入商品列表

示例:實現商品列表的無限滾動載入。

@Entry
@Component
struct InfiniteScrollExample {
    private products: string[] = Array.from({ length: 20 }, (_, i) => `商品 ${i + 1}`);
    private pageCount: number = 1;

    build() {
        Column() {
            LazyForEach(this.products, (product) => {
                Text(product).fontSize(16).padding(10)
            })

            Button("載入更多") {
                this.loadMoreProducts(); // 載入更多商品
            }.padding(10)
        }
    }

    private loadMoreProducts() {
        // 模擬每次載入 20 個商品
        this.pageCount += 1;
        const newProducts = Array.from({ length: 20 }, (_, i) => `商品 ${i + 1 + this.products.length}`);
        this.products.push(...newProducts);
    }
}

解析

  • 初始資料為 20 個商品,點選按鈕後動態載入更多商品。
  • LazyForEach 按需渲染新增的內容,保持效能穩定。

場景 3:聊天訊息介面

示例:渲染實時更新的聊天訊息。

@Entry
@Component
struct ChatExample {
    private messages: { sender: string; content: string }[] = [
        { sender: "使用者A", content: "你好" },
        { sender: "使用者B", content: "你好!有事嗎?" }
    ];

    build() {
        LazyForEach(this.messages, (message) => {
            Column() {
                Text(`${message.sender}:`).fontSize(14).fontWeight(FontWeight.Bold).padding(2)
                Text(message.content).fontSize(16).padding(5)
            }.padding(10).border(Color.Gray, 1).margin(5)
        })
    }

    addMessage(sender: string, content: string) {
        this.messages.push({ sender, content });
    }
}

解析

  • 聊天訊息實時追加到資料集合 messages 中。
  • LazyForEach 僅渲染當前可見區域的訊息,確保滾動和渲染的流暢性。

場景 4:渲染含圖片的大規模資料

示例:展示 500 張商品圖片列表。

@Entry
@Component
struct ImageGridExample {
    private images: string[] = Array.from({ length: 500 }, (_, i) => `https://example.com/image${i + 1}.jpg`);

    build() {
        LazyForEach(this.images, (imageUrl) => {
            Image(imageUrl).width(100).height(100).margin(5)
        })
    }
}

解析

  • 渲染圖片時記憶體消耗較高,LazyForEach 可以有效避免載入所有圖片造成的卡頓。
  • 僅在使用者滾動到圖片所在位置時載入對應內容。

LazyForEach 的優勢

  1. 效能最佳化

    • 避免一次性渲染整個資料集合,顯著減少記憶體和 CPU 的佔用。
    • 在資料規模較大時(如幾千條以上),相比 ForEach 更高效。
  2. 按需更新

    • 渲染機制基於使用者可見範圍,動態更新介面內容。
  3. 適應動態資料

    • 動態增刪資料集合中的元素時,LazyForEach 無需手動干預,即可自動最佳化。

注意事項

  1. 適用場景

    • 推薦:用於大規模資料集合(數百條及以上),如聊天記錄、長列表、圖片網格等。
    • 非必要:若資料規模較小(幾百條以內),可以使用 ForEach,避免引入額外複雜性。
  2. 檢視更新

    • 當資料集合變化較頻繁時,確保資料的唯一標識(如索引或 ID)以避免檢視錯亂。
  3. 資源管理

    • 如果渲染內容包含較大的資源(如圖片、影片),需要適當處理資源載入與釋放。

LazyForEach 是 ArkTS 中不可或缺的渲染工具,它以延遲載入的機制為大規模動態資料集合的顯示提供了效能保障,同時保證使用者互動的流暢體驗。在開發長列表或大規模資料的應用時,它能顯著提高開發效率和應用效能。

4. ContentSlot 渲染控制

ContentSlot 是 ArkTS 提供的一種 插槽渲染機制,用於在元件中動態插入子元件內容。它允許父元件定義佔位區域,子元件可以透過插槽填充動態內容。類似於前端開發中的 slotchildren 概念,ContentSlot 提高了元件的複用性和靈活性。

主要用途

  1. 元件內容動態填充

    • 透過佔位插槽機制,父元件定義好插槽位置,具體內容由子元件或外部動態提供。
  2. 高複用性元件開發

    • 設計通用元件時,開發者可以透過插槽允許自定義內容插入,例如按鈕、彈窗等。
  3. 父子元件的靈活傳遞

    • ContentSlot 使父元件與子元件的內容解耦,減少硬編碼的限制。
  4. 提升內容擴充套件能力

    • 插槽內容可以根據需求靈活調整,適應不同場景的 UI 設計。

語法

定義插槽

ContentSlot("slotName", defaultContent)

填充插槽

Column() {
    CustomComponent {
        slotName: Text("我是WG")
    }
}
  • slotName:插槽的名稱,父元件透過這個名稱標識插槽位置。
  • defaultContent:預設顯示的內容,如果未插入內容,將使用該內容。

示例與場景講解

場景 1:動態按鈕內容

示例:建立一個按鈕元件,支援動態插入內容(如文字或圖示)。

@Entry
@Component
struct ButtonWithSlot {
    build() {
        Column() {
            // 使用預設內容
            CustomButton()
            
            // 動態插入內容
            CustomButton {
                contentSlot: Text("提交").fontSize(18).fontWeight(FontWeight.Bold)
            }
        }
    }
}

@Component
struct CustomButton {
    build() {
        Row()
            .backgroundColor(Color.Blue)
            .padding(10)
            .alignItems(HorizontalAlign.Center)
            .borderRadius(5) {
            ContentSlot("contentSlot", Text(" 點我一下試試").fontSize(16).color(Color.White))
        }
    }
}

解析

  • CustomButton 是一個通用按鈕元件,使用 ContentSlot 定義插槽 contentSlot
  • 預設插槽內容為 "預設按鈕",外部可以動態插入不同的內容,如 "提交"。

場景 2:可擴充套件的彈窗元件

示例:實現一個通用彈窗,支援動態插入標題和內容。

@Entry
@Component
struct PopupWithSlotExample {
    private isVisible: boolean = true;

    build() {
        if (this.isVisible) {
            Popup {
                titleSlot: Text("彈窗標題:點了又能怎樣").fontSize(20).fontWeight(FontWeight.Bold),
                contentSlot: Text("這是彈窗的內容:不怎麼樣,你贏了").fontSize(16).padding(10)
            }
        }
    }
}

@Component
struct Popup {
    build() {
        Column()
            .backgroundColor(Color.White)
            .padding(20)
            .borderRadius(10) {
            ContentSlot("titleSlot", Text("預設標題").fontSize(18).color(Color.Black))
            Divider().backgroundColor(Color.Gray).height(1).margin(10, 0)
            ContentSlot("contentSlot", Text("預設內容").fontSize(14).color(Color.Gray))
        }
    }
}

解析

  • Popup 是一個彈窗元件,定義了 titleSlotcontentSlot 兩個插槽。
  • 父元件 PopupWithSlotExample 透過插槽填充標題和內容,外部元件可以靈活調整彈窗內容。

場景 3:動態卡片元件

示例:實現一個支援自定義圖片和文字內容的卡片元件。

@Entry
@Component
struct CardSlotExample {
    build() {
        Card {
            imageSlot: Image("https://xxxxx.com/image.jpg").width(100).height(100),
            textSlot: Text("自定義卡片內容").fontSize(18).padding(10)
        }
    }
}

@Component
struct Card {
    build() {
        Column()
            .backgroundColor(Color.LightGray)
            .padding(10)
            .borderRadius(8)
            .alignItems(HorizontalAlign.Center) {
            ContentSlot("imageSlot", Image("").width(50).height(50))
            ContentSlot("textSlot", Text("預設卡片內容").fontSize(16).color(Color.Black))
        }
    }
}

解析

  • Card 定義了 imageSlottextSlot,允許父元件動態插入圖片和文字內容。
  • 父元件透過填充插槽,自定義卡片的具體顯示內容。

ContentSlot 的優勢

  1. 靈活性

    • 插槽內容完全由外部定義,元件可以適應多種使用場景。
  2. 高複用性

    • 通用元件開發中,透過 ContentSlot 提供內容插入點,大大提高元件的複用性。
  3. 內容解耦

    • 父元件負責插槽定義,子元件填充具體內容,減少程式碼耦合。
  4. 預設內容支援

    • 插槽未填充時,顯示預設內容,保證元件的完整性和容錯性。

注意事項

  1. 插槽命名

    • 插槽名稱應具有描述性,避免重名導致內容錯亂。
  2. 預設內容設定

    • 始終為插槽提供預設內容,防止外部未填充時出現空白區域。
  3. 複雜巢狀場景

    • 在巢狀元件中使用 ContentSlot 時,保持插槽結構清晰,避免邏輯混亂。

ContentSlot 為 ArkTS 的元件設計提供了高度的擴充套件能力,開發者可以透過它實現動態化和模組化的元件設計。無論是按鈕、彈窗,還是複雜的卡片元件,ContentSlot 都能讓內容的插入變得簡單而高效。


小結一下

控制方式 功能 典型應用場景
if/else 條件控制渲染元件的顯示與隱藏 動態顯示狀態內容(如登入/未登入狀態)
ForEach 遍歷小規模資料集合渲染元件 渲染固定數量的列表
LazyForEach 延遲渲染大規模資料集合 動態載入的長列表(如訊息、資料流)
ContentSlot 支援動態插入子元件 插槽設計,實現元件內容的動態擴充

我們可以充分利用 ArkTS 提供的宣告式渲染控制能力,提升應用的靈活性與效能。關注威哥愛程式設計,鴻蒙千帆起。

相關文章