鸿蒙 ArkTS:用 LazyForEach 给单词打卡 App 的错词列表提速——长列表滚动卡顿实录
背景:11 月 10 日我用 AI DevSpace 免费体验版给鸿蒙单词打卡 App 加了一个错词本;错词攒到 300 多条后,错词列表页滚动开始卡顿。本文记录把 ForEach 换成 LazyForEach 的一次优化实录,不重复讲错词本怎么做,只讲长列表为什么卡、怎么改、改完效果如何。
一、卡顿的现象
- 错词超过 300 条时,第一次进入错词列表页要等约 1 秒;
- 快速上下滑动时掉帧,偶尔出现短暂白块;
- 列表越长,内存占用越高,低端设备更明显。
二、为什么 ForEach 会卡
ForEach 会一次性为数据源里的每一项创建组件,300 条错词就是 300 多个 ListItem,哪怕大部分根本不在屏幕里。首屏要做的创建工作太多,滑动时要维护的节点也多,自然就卡。
LazyForEach 的思路正好反过来:只创建屏幕内可见项对应的组件,滑到哪儿、建到哪儿,滑出屏幕的组件还会被复用。长列表场景下,首屏创建量从几百个降到十几个。
三、改造:三步把 ForEach 换成 LazyForEach
1. 实现一个 IDataSource 数据源
class WrongWordDataSource implements IDataSource {
private dataArray: WrongWord[] = [];
private listeners: DataChangeListener[] = [];
constructor(data: WrongWord[]) {
this.dataArray = data;
}
totalCount(): number {
return this.dataArray.length;
}
getData(index: number): WrongWord {
return this.dataArray[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => listener.onDataReloaded());
}
addWord(word: WrongWord): void {
this.dataArray.push(word);
this.listeners.forEach(listener => {
listener.onDataAdd(this.dataArray.length - 1);
});
}
}
2. 在 List 里用 LazyForEach,并给稳定的 key
List() {
LazyForEach(this.dataSource, (item: WrongWord) => {
ListItem() {
WrongWordRow({ word: item })
}
}, (item: WrongWord) => item.id.toString())
}
key 一定要用错词本身稳定的 id,不能用数组下标。下标会在增删后错位,导致复用的行显示成别的词——这是我第一次改时真实踩到的坑。
3. 数据变化后记得通知
新增错词、删除已掌握的词、整批导入后,都要调对应的通知方法(onDataAdd、onDataDelete、onDataReloaded 等),否则数据源变了,界面不会刷新。配合 11 月 11 日文章里讲的 @Observed 与 @ObjectLink,条目内部的“已掌握”状态变化也能同步回列表。
四、实测对比(同一台模拟器、同一份 300 条错词数据)
| 指标 | ForEach | LazyForEach |
|---|---|---|
| 首次进入列表 | 约 1.0 秒 | 约 0.3 秒 |
| 快速滑动 | 掉帧、偶发白块 | 基本流畅 |
| 初始创建组件数 | 300 多个 | 屏幕内十余个 |
说明:以上是同一台模拟器上的粗略对比,不是严格基准测试,但方向很明确——长列表优先用 LazyForEach。
五、顺手提一句:数据源骨架是 AI Shell 帮我搭的
这次改造的数据源骨架,是在 AI DevSpace 免费体验版里新建会话,让 AI Shell 按 IDataSource 接口先生成模板,我再按错词本的业务逻辑补上增删方法和通知调用。AI 生成的代码要自己逐行检查,比如它一开始给的 key 用的是下标,我手工改成了 id。
六、小结
- 列表超过一屏、数据量会持续增长时,优先用 LazyForEach + IDataSource;
- key 必须稳定唯一,不要用下标;
- 数据源变化后一定要通知,界面才会刷新;
- 优化之前先看现象、之后再量一下,改动才有依据。
下一篇准备写错词本的搜索与按掌握程度筛选,欢迎交流。
- 点赞
- 收藏
- 关注作者
评论(0)