写点什么

ArkTS 四种渲染控制能力

作者:威哥爱编程
  • 2024-11-25
    北京
  • 本文字数:7861 字

    阅读完需:约 26 分钟

大家好,我是 V 哥。ArkTS 是 OpenHarmony 框架的一部分,提供了声明式 UI 渲染的能力。下面来对 ArkTS 中四种渲染控制能力: if/elseForEachLazyForEachContentSlot 详细介绍一下:

1. if/else 渲染控制

if/else 是 ArkTS 提供的基本逻辑控制语法,用于 基于条件动态控制组件的渲染。它的核心用途是根据某些条件,在运行时决定渲染哪些组件,以及组件的结构或内容。以下是它的主要特点和用途的详细介绍:

主要用途

  1. 动态显示或隐藏组件

  2. 根据变量的值,动态控制某些组件是否渲染。

  3. 避免不必要的组件渲染,提高性能。

  4. 实现多种状态的界面切换

  5. 适合条件分支较少的场景。

  6. 在界面上根据状态显示不同的布局或信息(如登录状态、加载中状态、错误提示等)。

  7. 响应用户交互或数据变化

  8. 基于用户的操作动态更新界面。

  9. 如点击按钮后切换视图,或数据加载完成后切换显示内容。

  10. 个性化内容显示

  11. 根据用户角色、权限或其他业务逻辑,动态展示不同的组件或内容。



详细讲解

语法基础


  • if/else 语法与普通编程语言一致,可以嵌套其他 ArkTS UI 组件。

  • if 用于条件成立时渲染的组件,else 用于条件不成立时的渲染。



示例场景与代码

场景 1:登录状态控制

根据用户是否登录,显示欢迎信息或登录提示。


@Entry@Componentstruct 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@Componentstruct 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@Componentstruct 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. 嵌套层级过深会影响代码的可读性,建议将复杂逻辑拆分成方法或子组件。

  3. 性能优化

  4. if/else 不会渲染未满足条件的组件,因此可以利用该特性控制渲染数量,避免浪费资源。

  5. 配合状态管理

  6. 可以结合 State@Observed 数据模型,实现更灵活的动态渲染。


if/else 是 ArkTS 中最基础的控制语法,通过动态切换组件的渲染路径,可以灵活应对各种场景下的界面需求,使开发者能够快速实现逻辑清晰、性能优异的动态 UI。



2. ForEach 渲染控制

ForEach 是 ArkTS 提供的一种迭代语法,专用于遍历固定或小规模的数据集合,并基于集合的每个元素动态生成 UI 组件。它在开发中扮演着重要角色,尤其是需要根据数组或列表生成界面内容时。

主要用途

  1. 动态渲染组件列表

  2. 根据数据集合的元素,生成对应的组件。

  3. 适合中小规模的静态或动态数据集合。

  4. 数据驱动的视图更新

  5. 数据集合发生改变(增删改)时,ForEach 会自动更新渲染内容,保证视图与数据同步。

  6. 可配置的动态视图

  7. 使用相同的组件模板,为不同的数据生成独特的内容或样式。

  8. 清晰的逻辑分离

  9. 数据与视图逻辑解耦,通过数据集合定义 UI,避免手动创建多个类似组件。



语法

基本语法


ForEach(array, (item) => {    // 渲染逻辑})
复制代码


参数


  • array:要遍历的数组或集合。

  • (item):当前元素的引用,可用于组件内容的动态生成。



示例与场景讲解

场景 1:简单列表渲染

示例:展示水果名称的简单列表。


@Entry@Componentstruct SimpleListExample {    private fruits: string[] = ["苹果", "香蕉", "橘子", "葡萄"];
build() { Column() { ForEach(this.fruits, (fruit) => { Text(fruit).fontSize(20).padding(5) }) } }}
复制代码


解析


  • 遍历 fruits 数组,动态生成每个 Text 组件。

  • fruits 数组发生变化时(增删项),界面会自动更新。



场景 2:带索引的列表渲染

示例:显示带序号的城市列表。


@Entry@Componentstruct 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@Componentstruct 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@Componentstruct 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. 适用于小规模数据集合

  2. ForEach 适合几百条以内的数据,数据量特别大时建议使用 LazyForEach

  3. 保持数据唯一性

  4. 遍历的数据集合应尽量保持唯一标识(如索引或 ID),避免意外更新或视图错乱。

  5. 避免过深嵌套

  6. 如果 ForEach 嵌套过多,可能导致代码不易维护,建议拆分为子组件。




通过 ForEach 渲染控制,开发者能够快速生成基于数据的动态 UI,满足各种场景下的灵活需求,从静态内容到动态交互都可以轻松实现。

3. LazyForEach 渲染控制

LazyForEach 是 ArkTS 提供的一种高效迭代语法,专为 大规模、动态数据集合 设计,通过 延迟加载 的机制,按需渲染可见范围内的组件,从而显著提高性能。

主要用途

  1. 大规模数据的高效渲染

  2. 当数据集合规模较大(如数千条以上),LazyForEach 能避免一次性加载所有组件,从而节省内存和渲染时间。

  3. 按需加载数据

  4. 根据当前视图的可见范围,仅渲染需要显示的内容,未进入视图的部分不会占用计算资源。

  5. 支持动态增删数据

  6. 当数据集合动态变化(增删、更新)时,LazyForEach 会自动优化渲染流程,保持性能稳定。

  7. 流畅的用户体验

  8. 在需要滚动查看长列表(如聊天记录、商品列表)时,延迟加载机制保证了操作的流畅性。



语法

先来看一下基本语法


LazyForEach(array, (item, index) => {    // 渲染逻辑})
复制代码


参数


  • array:需要遍历的大规模数据集合。

  • (item, index):当前数据元素及其索引,传递给渲染逻辑。



示例与场景讲解

场景 1:大规模数据的列表渲染

示例:显示 1,000 条用户消息记录。


@Entry@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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. 性能优化

  2. 避免一次性渲染整个数据集合,显著减少内存和 CPU 的占用。

  3. 在数据规模较大时(如几千条以上),相比 ForEach 更高效。

  4. 按需更新

  5. 渲染机制基于用户可见范围,动态更新界面内容。

  6. 适应动态数据

  7. 动态增删数据集合中的元素时,LazyForEach 无需手动干预,即可自动优化。



注意事项

  1. 适用场景

  2. 推荐:用于大规模数据集合(数百条及以上),如聊天记录、长列表、图片网格等。

  3. 非必要:若数据规模较小(几百条以内),可以使用 ForEach,避免引入额外复杂性。

  4. 视图更新

  5. 当数据集合变化较频繁时,确保数据的唯一标识(如索引或 ID)以避免视图错乱。

  6. 资源管理

  7. 如果渲染内容包含较大的资源(如图片、视频),需要适当处理资源加载与释放。


LazyForEach 是 ArkTS 中不可或缺的渲染工具,它以延迟加载的机制为大规模动态数据集合的显示提供了性能保障,同时保证用户交互的流畅体验。在开发长列表或大规模数据的应用时,它能显著提高开发效率和应用性能。

4. ContentSlot 渲染控制

ContentSlot 是 ArkTS 提供的一种 插槽渲染机制,用于在组件中动态插入子组件内容。它允许父组件定义占位区域,子组件可以通过插槽填充动态内容。类似于前端开发中的 slotchildren 概念,ContentSlot 提高了组件的复用性和灵活性。

主要用途

  1. 组件内容动态填充

  2. 通过占位插槽机制,父组件定义好插槽位置,具体内容由子组件或外部动态提供。

  3. 高复用性组件开发

  4. 设计通用组件时,开发者可以通过插槽允许自定义内容插入,例如按钮、弹窗等。

  5. 父子组件的灵活传递

  6. ContentSlot 使父组件与子组件的内容解耦,减少硬编码的限制。

  7. 提升内容扩展能力

  8. 插槽内容可以根据需求灵活调整,适应不同场景的 UI 设计。



语法

定义插槽


ContentSlot("slotName", defaultContent)
复制代码


填充插槽


Column() {    CustomComponent {        slotName: Text("我是WG")    }}
复制代码


  • slotName:插槽的名称,父组件通过这个名称标识插槽位置。

  • defaultContent:默认显示的内容,如果未插入内容,将使用该内容。



示例与场景讲解

场景 1:动态按钮内容

示例:创建一个按钮组件,支持动态插入内容(如文本或图标)。


@Entry@Componentstruct ButtonWithSlot {    build() {        Column() {            // 使用默认内容            CustomButton()                        // 动态插入内容            CustomButton {                contentSlot: Text("提交").fontSize(18).fontWeight(FontWeight.Bold)            }        }    }}
@Componentstruct 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@Componentstruct PopupWithSlotExample {    private isVisible: boolean = true;
build() { if (this.isVisible) { Popup { titleSlot: Text("弹窗标题:点了又能怎样").fontSize(20).fontWeight(FontWeight.Bold), contentSlot: Text("这是弹窗的内容:不怎么样,你赢了").fontSize(16).padding(10) } } }}
@Componentstruct 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@Componentstruct CardSlotExample {    build() {        Card {            imageSlot: Image("https://xxxxx.com/image.jpg").width(100).height(100),            textSlot: Text("自定义卡片内容").fontSize(18).padding(10)        }    }}
@Componentstruct 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. 插槽内容完全由外部定义,组件可以适应多种使用场景。

  3. 高复用性

  4. 通用组件开发中,通过 ContentSlot 提供内容插入点,大大提高组件的复用性。

  5. 内容解耦

  6. 父组件负责插槽定义,子组件填充具体内容,减少代码耦合。

  7. 默认内容支持

  8. 插槽未填充时,显示默认内容,保证组件的完整性和容错性。



注意事项

  1. 插槽命名

  2. 插槽名称应具有描述性,避免重名导致内容错乱。

  3. 默认内容设置

  4. 始终为插槽提供默认内容,防止外部未填充时出现空白区域。

  5. 复杂嵌套场景

  6. 在嵌套组件中使用 ContentSlot 时,保持插槽结构清晰,避免逻辑混乱。


ContentSlot 为 ArkTS 的组件设计提供了高度的扩展能力,开发者可以通过它实现动态化和模块化的组件设计。无论是按钮、弹窗,还是复杂的卡片组件,ContentSlot 都能让内容的插入变得简单而高效。



小结一下


我们可以充分利用 ArkTS 提供的声明式渲染控制能力,提升应用的灵活性与性能。关注威哥爱编程,鸿蒙千帆起。

发布于: 刚刚阅读数: 5
用户头像

华为 HDE、CSDN 博客专家、Java畅销书作者 2018-05-30 加入

全栈领域优质创作者(Java/HarmonyOS/AI),公众号:威哥爱编程

评论

发布
暂无评论
ArkTS四种渲染控制能力_华为_威哥爱编程_InfoQ写作社区