HarmonyOS 7 性能优化:List 长列表懒加载与缓存实践

做鸿蒙开发的时候,长列表是绕不开的场景。不管是信息流、商品列表还是设置页,只要数据量上了百条,滚动卡顿、掉帧就很容易出现。最近在项目里优化一个 500+ 条数据的列表,把 FPS 从 30 拉到了稳定 60,这里把踩过的坑和优化思路整理一下。

问题现象

我们的列表页是商品列表,每个 item 包含一张图片、标题、价格和标签。数据量大概在 500 条左右,初始加载就有明显的白屏和卡顿,快速滚动的时候掉帧严重,低端机上甚至能感觉到明显的撕裂。

一开始我以为是图片加载的问题,加了图片缓存之后效果不明显。后来用 Profiler 一查,发现瓶颈根本不在图片,而是在 List 组件本身------每滚动一屏,都在反复创建和销毁组件,渲染线程压力很大。

优化思路

ArkUI 的 List 组件其实自带懒加载能力,但很多人用的时候没开对参数。核心是三个点:

  • cachedCount :预渲染屏幕外的 item 数量,滚动时不会临时创建

  • layout :用 LinearLayout 而不是默认的,配合 cachedCount 效果更好

  • keyGenerator:给每个 item 指定稳定的 key,减少不必要的组件重建

另外,自定义组件要拆得够细,不要把整个 item 的渲染都塞在一个 build 方法里。拆成子组件之后,配合 @State 和 @Prop,只有变化的部分才会重新渲染。

代码实现

下面是优化后的完整代码,包含了懒加载配置和 item 拆分。

1. 列表组件配置

typescript 复制代码
// ProductListPage.ets
@Entry
@Component
struct ProductListPage {
  @State products: Product[] = [];
  private listController: ListController = new ListController();

  aboutToAppear() {
    this.loadData();
  }

  build() {
    List({ space: 8, controller: this.listController }) {
      ForEach(this.products, (item: Product) => {
        ListItem() {
          ProductItem({ product: item })
        }
        // 关键:给每个 item 稳定的 key
      }, (item: Product) => item.id)
    }
    // 关键:预渲染 10 个屏外 item
    .cachedCount(10)
    // 关键:用 LinearLayout 布局,配合缓存效果更好
    .layout(LinearLayout)
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
  }

  private loadData() {
    // 模拟加载 500 条数据
    this.products = Array.from({ length: 500 }, (_, i) => ({
      id: `product_${i}`,
      title: `商品 ${i}`,
      price: Math.floor(Math.random() * 1000) + 100,
      image: `https://picsum.photos/200/200?random=${i}`
    }));
  }
}

2. 拆分 item 组件

typescript 复制代码
// ProductItem.ets
@Component
struct ProductItem {
  @Prop product: Product;

  build() {
    Row({ space: 12 }) {
      // 图片单独渲染
      Image(this.product.image)
        .width(80)
        .height(80)
        .borderRadius(8)
        .objectFit(ImageFit.Cover)

      Column({ space: 4 }) {
        // 标题
        Text(this.product.title)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })

        // 价格
        Text(`¥${this.product.price}`)
          .fontSize(18)
          .fontColor('#ff4400')
          .fontWeight(FontWeight.Bold)
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .padding(12)
    .backgroundColor('#ffffff')
    .borderRadius(8)
  }
}

踩坑总结

1. cachedCount 不是越大越好

一开始我把 cachedCount 设成了 50,结果内存直接爆了。预渲染太多 item 会占用大量内存,反而得不偿失。一般设成 5~10 就够了,具体数值要在真机上测。

2. keyGenerator 一定要传

不传 key 的话,ForEach 默认用索引做 key,数据刷新的时候整个列表都会重新渲染。传了稳定的 id 之后,只有新增或删除的 item 才会重建,性能提升非常明显。

3. 图片要用本地缓存

网络图片每次滚动回来都重新加载,肯定卡。HarmonyOS 7 的 Image 组件默认有内存缓存,但建议自己再加一层磁盘缓存,避免重复请求。

4. 低端机要降帧

优化完之后高端机满帧,但低端机还是偶尔掉帧。这时候可以在低端机上降低图片分辨率,或者关闭一些特效,保证基本流畅度。

效果对比

优化前:500 条数据,快速滚动时 FPS 30~40,明显卡顿。

优化后:同样数据量,FPS 稳定 55~60,快速滚动基本无感。

性能优化这件事,别靠猜,用 Profiler 量出来的瓶颈才是真瓶颈。我们一开始以为是图片的问题,结果折腾了半天图片缓存,最后发现是 List 本身没开缓存。

总结一下就是:List 一定要开 cachedCount,ForEach 一定要传 keyGenerator,item 要拆成独立组件,图片要做缓存。这三点做到位,大部分长列表的性能问题都能解决。

相关推荐
威哥爱编程2 小时前
HarmonyOS 碰一碰与隔空传送实战:真正的坑在 3 秒铁律和生命周期
harmonyos
威哥爱编程2 小时前
HarmonyOS 扫码直达接入实战:系统扫、应用落,三步送用户进履约页
华为·harmonyos·arkts
威哥爱编程2 小时前
HarmonyOS 6.0 智感握姿实战:一道安检门、五态分诊、一静一响两个坑
华为·harmonyos·arkts
李游Leo2 小时前
HarmonyOS 7 音频与媒体控制实战 02:实现系统音频内录与状态控制
harmonyos
李游Leo3 小时前
HarmonyOS 7 音频与媒体控制实战 05:排查播放无声、卡顿和杂音问题
harmonyos
大雷神3 小时前
【共创稿事节】HarmonyOS ArkGraphics 3D 实操:用 GLB 节点与 PBR 材质打造智能音箱选配器
harmonyos
贾伟康11 小时前
【HarmonyOS 7新能力|020】LazyLayoutAlgorithm入门实战:从能力边界到最小可运行链路
harmonyos·arkts·arkui·harmonyos 7·lazylayout
梦想不只是梦与想13 小时前
鸿蒙 邀请测试:发布测试版本
harmonyos·appgallery 邀请测试·邀请测试
马剑威(威哥爱编程)15 小时前
【共创稿事节】HarmonyOS 7 应用 Skill 化实战:从“被打开“到“被调用“,把功能递进系统意图分发池
pytorch·深度学习·harmonyos