鸿蒙 ArkTS:用 LazyForEach 给单词打卡 App 的错词列表提速——长列表滚动卡顿实录

举报
yd_276656611 发表于 2026/11/12 10:01:12 2026/11/12
【摘要】 背景:11 月 10 日我用 AI DevSpace 免费体验版给鸿蒙单词打卡 App 加了一个错词本;错词攒到 300 多条后,错词列表页滚动开始卡顿。本文记录把 ForEach 换成 LazyForEach 的一次优化实录,不重复讲错词本怎么做,只讲长列表为什么卡、怎么改、改完效果如何。 一、卡顿的现象错词超过 300 条时,第一次进入错词列表页要等约 1 秒;快速上下滑动时掉帧,偶尔出...

背景: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 必须稳定唯一,不要用下标;
  • 数据源变化后一定要通知,界面才会刷新;
  • 优化之前先看现象、之后再量一下,改动才有依据。

下一篇准备写错词本的搜索与按掌握程度筛选,欢迎交流。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。