写点什么

【HarmonyOS Next】鸿蒙循环渲染 ForEach,LazyForEach,Repeat 使用心得体会

作者:GeorgeGcs
  • 2025-03-24
    上海
  • 本文字数:1907 字

    阅读完需:约 6 分钟

【HarmonyOS Next】鸿蒙循环渲染ForEach,LazyForEach,Repeat使用心得体会

【HarmonyOS Next】鸿蒙循环渲染 ForEach,LazyForEach,Repeat 使用心得体会

一、ForEach,LazyForEach,Repeat 三者关系

在鸿蒙中列表组件循环渲染的实现,一般都是通过 ForEach 来配合列表容器组件实现,例如 List。也可以直接 ForEach 去创建多个相同的 View。


以下代码,是个简单的 ForEach 使用。在 list 组件中通过包裹 ForEach 来循环渲染创建子组件。数据通过传入数组对象的形式,批量一次性循环渲染更新完数据。


需要注意的是,在 ForEach 组件进行非首次渲染时,它会检查新生成的键值是否在上次渲染中已经存在。如果键值不存在,则会创建一个新的组件;如果键值存在,则不会创建新的组件,而是直接渲染该键值所对应的组件。


作为移动开发转鸿蒙,笔者喜欢将列表容器的子组件称之为,ItemVIew。


渲染最终效果是,自上而下显示三行文本的垂直列表。


@Entry@Componentstruct ListPage {  @State tempList: Array<string> = ['第一行代码', '开发艺术探索', '入门到精通'];
build() { Row() { List() { ForEach(this.tempList, (item: string) => { ListItem(){ ItemView ({ item: item }) .margin({top: 30}) } }, (item: string) => item) } .width('100%') .height('100%') } .alignListItem(ListItemAlign.Center) .height('100%')
}}
@Componentstruct ItemView { @Prop item: string;
build() { Text(this.item) .fontSize(50) }}
复制代码


使用起来相当简单,但是实际业务开发中,大批量的列表数据渲染还是用这种方式,在首次加载时会造成性能卡顿。例如我有一万条数据,那就需要创建一万个 ItemVIew,渲染压力可见非常恐怖。


为了解决该问题,所以 LazyForEach 应运而生。这两者主要的区别就是数据不一次性加载渲染,**懒加载通过提供的数据源中按需迭代数据。**通过继承 BasicDataSource 来监听控制列表的数据源进行按需加载。


框架会根据滚动容器可视区域按需创建组件,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。【比起 Android 和 IOS 的回收机制,这里设计的并不完美。因为没有复用机制。】


正因为 LazyForEach 的不足点,所以 V2 中又提供了 Repeat 来实现循环回收复用机制,也简化了数据源控制刷新的模板代码。

二、ForEach,LazyForEach 的局限性

ForEach1.只能一次性渲染列表内所有数据去创建 VIew,没办法按需加载,性能损耗严重,内存占用大。2.索引自己维护,容易造成不可知的 UI 问题。例如索引一致后,UI 刷新会异常。


LazyForEach:1.只能在容器列表组件中使用 2.数据源的样板配置代码太过于冗余 3.回收机制,没有复用 View,快速列表时,还是会有性能损耗

三、Repeat 的优点和和使用介绍

Repeat 提供了两种模式 non-virtualScroll 模式和 virtualScroll 模式。


non-virtualScroll 模式类似于 ForEach 的使用。渲染短数据列表、组件全部加载的场景。


@Entry@ComponentV2struct ListPage {  @Local tempList: Array<string> = ['第一行代码', '开发艺术探索', '入门到精通'];
build() { Row() { Column() { Text('点击修改第1个数组项的值') .fontSize(24) .fontColor(Color.Red) .onClick(() => { this.simpleList[0] = 'HarmonyOS第一行代码'; })
Repeat<string>(this.tempList) .each((res: RepeatItem<string>)=>{ ItemView({ item: res.item }) .margin({top: 30}) }) .key((item: string) => item) } .justifyContent(FlexAlign.Center) .width('100%') .height('100%') } .height('100%') .backgroundColor(0xF1F3F5) }}
@ComponentV2struct ItemView { @Param @Require data: string;
build() { Text(this.data) .fontSize(30) }}
复制代码


virtualScroll 模式类似于 LazyForEach 的使用。渲染需要懒加载的长数据列表、通过组件复用优化性能表现的场景。需要创建模板代码,用于复用 view。 点击跳转参考官方示例代码



在 Repeat 首次渲染时,根据容器组件的有效加载范围(可视区域+预加载区域)创建当前需要的子组件。


在容器滑动/数组改变时,将失效的子组件节点(离开有效加载范围)加入空闲节点缓存列表中(断开与组件树的关系,但不销毁),在需要生成新的组件时,对缓存里的组件进行复用(更新被复用子组件的变量值,重新上树)。


需要注意的是,Repeat 要配合 V2 状态管理装饰器来使用。virtualScroll 模式不支持 V1 装饰器,混用 V1 装饰器会导致渲染异常,不建议开发者同时使用。

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

GeorgeGcs

关注

路漫漫其修远兮,吾将上下而求索。 2024-12-24 加入

历经腾讯,宝马,研究所,金融。 待过私企,外企,央企。 深耕大应用开发领域十年。 OpenHarmony,HarmonyOS,Flutter,H5,Android,IOS。 目前任职鸿蒙应用架构师。 HarmonyOS官方认证创作先锋

评论

发布
暂无评论
【HarmonyOS Next】鸿蒙循环渲染ForEach,LazyForEach,Repeat使用心得体会_foreach_GeorgeGcs_InfoQ写作社区