对 HarmonyOS 列表与懒加载ArkTS技术实现:LazyForEach 长列表优化


一、引言
列表是移动应用中最常见的 UI 形态。无论是消息列表、商品列表、通讯录,还是社交动态,都离不开列表组件。当列表数据量很大(成百上千条)时,如果一次性全部渲染,会导致严重的内存占用和性能问题。HarmonyOS 提供了 LazyForEach(懒加载) 机制,配合 List 组件,可以高效处理长列表。
本文将以一个清新绿白风格的长列表演示页面为主线,深入讲解 List 组件、LazyForEach 懒加载、列表性能优化的核心技能。
二、List 组件基础
2.1 List 与 ListItem
List 是 ArkUI 的列表容器组件,ListItem 是列表项组件:
List({ space: 10 }) {
ListItem() {
Text('列表项一')
}
ListItem() {
Text('列表项二')
}
}
2.2 List 的核心属性
List({
space: 10, // 列表项间距
initialIndex: 0, // 初始滚动位置
scroller: this.scroller // 滚动控制器
}) {
// 列表项
}
.listDirection(Axis.Vertical) // 滚动方向
.scrollBar(BarState.Auto) // 滚动条
.edgeEffect(EdgeEffect.Spring) // 边缘效果
三、ForEach 与 LazyForEach
3.1 ForEach 的问题
ForEach 会一次性渲染所有列表项,当数据量很大时:
- 内存占用巨大。
- 首次渲染耗时。
- 滚动性能下降。
// 数据量大时性能差
List() {
ForEach(this.hugeArray, (item) => {
ListItem() {
Text(item)
}
})
}
3.2 LazyForEach 的优势
LazyForEach 采用懒加载策略:
- 按需渲染:只渲染当前可见区域的列表项。
- 组件复用:滚出可视区域的列表项会被回收复用。
- 内存友好:无论数据多大,内存中只保留可见项。
四、LazyForEach 的使用
4.1 数据源要求
LazyForEach 的数据源必须是 IDataSource 接口的实现类:
class MyDataSource implements IDataSource {
private data: string[] = [];
private listeners: DataChangeListener[] = [];
totalCount(): number {
return this.data.length;
}
getData(index: number): string {
return this.data[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
this.listeners.push(listener);
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const idx = this.listeners.indexOf(listener);
if (idx >= 0) {
this.listeners.splice(idx, 1);
}
}
// 数据变更通知
notifyDataReload(): void {
this.listeners.forEach((l) => l.onDataReloaded());
}
}
代码说明:
IDataSource 接口要求实现以下方法:
totalCount():返回数据总数。getData(index):根据索引返回数据。registerDataChangeListener/unregisterDataChangeListener:注册/注销数据变更监听。- 数据变化时需要通知监听器,如
onDataReloaded()。
4.2 使用 LazyForEach
List({ space: 10 }) {
LazyForEach(this.dataSource, (item: string) => {
ListItem() {
Text(item)
}
}, (item: string) => item) // 键生成器
}
代码说明:
LazyForEach第一个参数是数据源(IDataSource 实现)。- 第二个参数是渲染函数。
- 第三个参数是键生成器,用于唯一标识每个列表项,优化复用。
五、实战代码:长列表演示页面
下面我们实现一个清新绿白风格的长列表演示页面,包含吸顶标题、懒加载列表和悬浮按钮。
5.1 定义数据结构
interface ListItem {
id: number;
title: string;
desc: string;
tag: string;
}
代码说明:
ListItem 接口描述列表项数据:
id:唯一编号。title:标题。desc:描述文字。tag:标签(重要/普通/新增)。
5.2 初始化数据
@Entry
@Component
struct ListPage {
@State items: ListItem[] = [];
@State count: number = 50;
aboutToAppear(): void {
for (let i = 1; i <= 50; i++) {
this.items.push({
id: i,
title: `列表项 ${i}`,
desc: `这是第 ${i} 条数据,用于演示 LazyForEach 懒加载长列表的滚动性能`,
tag: i % 3 === 0 ? '重要' : (i % 3 === 1 ? '普通' : '新增')
});
}
}
代码说明:
aboutToAppear生命周期方法中初始化 50 条数据。- 通过
i % 3的余数分配不同标签,让列表看起来更真实。 @State count用于生成新数据的编号。
5.3 添加数据方法
addItem(): void {
this.count++;
this.items.push({
id: this.count,
title: `列表项 ${this.count}`,
desc: `这是第 ${this.count} 条数据,用于演示 LazyForEach 懒加载长列表的滚动性能`,
tag: '新增'
});
}
代码说明:
addItem 方法向列表追加一条新数据,演示列表的动态更新。点击悬浮按钮即可触发。
5.4 列表项构建器
@Builder
ItemCard(item: ListItem) {
Row({ space: 12 }) {
// 编号
Text(`${item.id}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.width(40)
.height(40)
.textAlign(TextAlign.Center)
.borderRadius(20)
.backgroundColor(item.tag === '重要' ? '#2ED573' : (item.tag === '新增' ? '#FFA502' : '#7BED9F'))
// 内容
Column({ space: 4 }) {
Row({ space: 8 }) {
Text(item.title)
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor('#2F3542')
Text(item.tag)
.fontSize(10)
.fontColor(Color.White)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor(item.tag === '重要' ? '#2ED573' : (item.tag === '新增' ? '#FFA502' : '#7BED9F'))
.borderRadius(8)
}
.alignItems(VerticalAlign.Center)
Text(item.desc)
.fontSize(12)
.fontColor('#747D8C')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
}
.width('100%')
.padding(14)
.backgroundColor(Color.White)
.borderRadius(14)
.shadow({ radius: 6, color: '#11000000', offsetY: 2 })
}
代码说明:
ItemCard 是列表项的 UI 构建器:
-
编号圆标:40x40 的圆形,根据标签类型使用不同颜色(重要=绿色、新增=橙色、普通=浅绿)。
-
标题与标签:标题加粗,标签是彩色小胶囊。
-
描述文字:
maxLines(2)限制最多两行,textOverflow超出部分显示省略号。 -
卡片样式:白色背景、圆角、轻微阴影,形成卡片列表效果。
5.5 构建 UI
build() {
Stack({ alignContent: Alignment.BottomEnd }) {
Column() {
// 顶部吸顶标题
Column() {
Text('LIST')
.fontSize(12)
.fontColor('#B8FFD8')
.letterSpacing(6)
Text('列表与懒加载')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.margin({ top: 4 })
Text(`共 ${this.items.length} 项 · LazyForEach 懒加载`)
.fontSize(12)
.fontColor('#B8FFD8')
.margin({ top: 4 })
}
.width('100%')
.padding({ top: 40, bottom: 20 })
.backgroundColor('#0E8A5F')
// List 长列表
List({ space: 10 }) {
ForEach(this.items, (item: ListItem) => {
ListItem() {
this.ItemCard(item)
}
}, (item: ListItem) => `${item.id}`)
}
.width('100%')
.layoutWeight(1)
.padding(12)
.scrollBar(BarState.Auto)
}
.width('100%')
.height('100%')
// 悬浮圆形按钮
Button() {
Text('+')
.fontSize(28)
.fontColor(Color.White)
}
.width(56)
.height(56)
.backgroundColor('#2ED573')
.borderRadius(28)
.shadow({ radius: 16, color: '#552ED573', offsetY: 4 })
.margin({ right: 20, bottom: 24 })
.onClick(() => { this.addItem(); })
}
.width('100%')
.height('100%')
.backgroundColor('#F0F5F2')
}
代码说明:
页面整体使用 Stack 布局,底层是列表,顶层是悬浮按钮:
-
吸顶标题:绿色背景的标题区,显示列表项总数。
-
List 长列表:
List({ space: 10 })创建列表,间距 10。ForEach渲染列表项(演示环境用 ForEach,生产环境长列表应使用 LazyForEach)。.layoutWeight(1)让列表占满剩余空间。.scrollBar(BarState.Auto)自动显示滚动条。
-
悬浮按钮:
Stack的alignContent: Alignment.BottomEnd将按钮定位在右下角,圆形绿色按钮带发光阴影。
六、List 高级用法
6.1 分组列表
List() {
// 分组标题
ListItem() {
Text('分组 A')
.fontSize(16)
.fontWeight(FontWeight.Bold)
}
.sticky(StickyStyle.Header) // 吸顶效果
// 分组内容
ForEach(groupA, (item) => {
ListItem() {
Text(item)
}
})
}
6.2 横向列表
List({ space: 10 }) {
ForEach(items, (item) => {
ListItem() {
Text(item)
}
})
}
.listDirection(Axis.Horizontal) // 横向滚动
6.3 列表滚动控制
// 创建滚动控制器
private scroller: Scroller = new Scroller();
// 滚动到指定位置
this.scroller.scrollToIndex(100);
this.scroller.scrollEdge(Edge.Top);
// 绑定到列表
List({ scroller: this.scroller }) {
// ...
}
七、列表性能优化
7.1 使用 LazyForEach
大数据量列表务必使用 LazyForEach,实现按需渲染:
List({ space: 10 }) {
LazyForEach(this.dataSource, (item: ListItem) => {
ListItem() {
this.ItemCard(item)
}
}, (item: ListItem) => `${item.id}`)
}
7.2 键生成器的重要性
键生成器用于唯一标识列表项。使用稳定的键(如数据 id)可以提升复用效率:
// 推荐:使用唯一 id
(item: ListItem) => `${item.id}`
// 不推荐:使用索引
(item: ListItem, index: number) => `${index}`
7.3 避免复杂布局
列表项布局应尽量简单,避免深层嵌套和复杂计算,减少渲染开销。
7.4 图片懒加载
列表中的图片应使用懒加载或占位图,避免一次性加载大量图片导致内存溢出。
八、常见问题
8.1 列表滚动卡顿
原因:列表项渲染复杂、数据量大、未使用懒加载。
解决:使用 LazyForEach,简化列表项布局。
8.2 列表项复用错乱
原因:键生成器不稳定或重复。
解决:使用唯一且稳定的键。
8.3 数据更新不刷新
原因:直接修改数组元素不触发 UI 刷新。
解决:使用新数组替换,或使用 IDataSource 的通知机制。
九、总结
本文深入讲解了 HarmonyOS 列表与懒加载技术,通过一个清新绿白风格的长列表演示页面实战演示了 List 组件、列表项构建、动态更新和悬浮按钮等核心能力。
核心要点回顾:
- List 是列表容器,ListItem 是列表项。
- ForEach 一次性渲染所有项,适合小数据量。
- LazyForEach 按需渲染,适合大数据量长列表。
- LazyForEach 数据源需实现 IDataSource 接口。
- 键生成器影响列表复用效率。
- 列表性能优化:懒加载、简化布局、稳定键。
列表是应用的基石,掌握懒加载和性能优化能让应用流畅处理海量数据。下一篇我们将讲解 HarmonyOS 表单与校验。
更多推荐

所有评论(0)