HarmonyOS 7 实战开发 03:长列表与动态内容性能优化
前两篇把工作台的基础框架和交互逻辑搭起来了,页面能跑了,但一加载真实数据就露馅——列表一滑就卡,掉帧掉到 30 以下,滚动的时候还有白屏闪烁。这篇专门讲长列表性能优化,也是鸿蒙开发里最容易出问题的地方。
我们项目中间内容区有个列表,数据量几百条起步,每条 item 还有封面图、标题、摘要、标签,一开始没做任何优化,在折叠屏上滑起来一顿一顿的。后来花了一周时间调优,才做到 60fps 流畅滚动。
问题出在哪:先定位再优化
不要一上来就改代码。先用 DevEco Studio 的 Profiler 工具看一下,到底是渲染慢、布局慢还是图片加载慢。我们当时用 Performance 面板录了一段滚动过程,发现两个问题:
第一,每次滚动都在重新创建列表项组件,没有复用。第二,每张封面图都是滚动到可视区域才开始加载,首屏渲染慢。
找到问题就好办了。接下来一步步改。
用 List 组件的懒加载和复用
ArkUI 的 List 组件本身是有懒加载机制的,但要用对。一开始我用 ForEach 循环渲染所有 item,结果一上来就把几百个组件都创建了,内存占用很高。
正确的做法是用 LazyForgen 或者直接用 List 的 itemGenerator。HarmonyOS 7 的 List 组件支持自动复用不在可视区域的 item,不需要手动管理。
下面是优化后的列表代码,文件位置在 components/ContentList.ets:
@Component
export struct ContentList {
@Prop items: ContentItem[];
onItemClick: (item: ContentItem) => void = () => {};
build() {
List({ space: 12 }) {
ForEach(this.items, (item: ContentItem) => {
ListItem() {
ContentCard({ item: item })
}
// 关键:设置 key 值,帮助 List 复用组件
}, (item: ContentItem) => item.id.toString())
}
.width('100%')
.layoutWeight(1)
.padding({ left: 16, right: 16, top: 12 })
// 预加载区域:可视区域外 2 个 item 就开始加载
.cachedCount(2)
}
}
@Component
struct ContentCard {
@ObjectLink item: ContentItem;
build() {
Row({ space: 12 }) {
// 封面图:设置宽度高度,避免布局抖动
Image(this.item.coverUrl)
.width(80)
.height(80)
.borderRadius(8)
// 图片加载失败占位图
.alt($r('app.media.placeholder'))
Column({ space: 6 }) {
Text(this.item.title)
.fontSize(15)
.fontWeight(FontWeight.Medium)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(this.item.summary)
.fontSize(13)
.fontColor('#666666')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Row({ space: 8 }) {
ForEach(this.item.tags, (tag: string) => {
Text(tag)
.fontSize(11)
.fontColor('#007aff')
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor('#e8f0fe')
.borderRadius(4)
}, (tag: string) => tag)
}
}
.layoutWeight(1)
.alignItems(ItemAlign.Start)
}
.width('100%')
.padding(12)
.backgroundColor('#ffffff')
.borderRadius(12)
}
}
这里有几个关键点:ForEach 第二个参数传了 item.id 作为 key,这样列表滚动的时候,组件会根据 key 复用,不会每次都创建新的。还有 cachedCount(2),意思是可视区域外预渲染 2 个 item,滚动的时候不会出现白屏。

图片加载优化
列表卡顿的另一个元凶是图片。每张封面图都是大图,直接加载原始分辨率会很卡。HarmonyOS 7 的 Image 组件支持自动降采样,但要注意几个点:
1. 明确设置宽高:不要让图片组件自己计算大小,否则每次布局都会重新测量,导致滚动卡顿。上面代码里 Image 直接写了 .width(80).height(80),布局性能提升很明显。
2. 使用占位图:.alt() 设置加载中的占位图,避免图片加载完成前布局跳动。
3. 图片缓存:同一篇文章的封面图不要重复加载,系统自带的图片缓存会自动处理,但要注意不要用不同的 URL 请求同一张图。
局部刷新,不要全量重绘
一开始我在列表 item 里用了 @State 存选中态,结果点了一个 item,整个列表都重新渲染了——因为父组件的 selectedItem 变了,所有 item 的 @Prop 都跟着变。
后来改成了 @ObjectLink + 状态管理类,每个 item 自己管理选中态,只有当前 item 的 UI 会更新,其他 item 不受影响。
核心思路:把频繁变化的状态拆到最小粒度的组件里,不要让一个状态变化触发大范围重绘。这在 ArkUI 里很重要,因为每次重绘都是有开销的。
性能分析工具的使用
调优的时候不要靠感觉,要用工具。DevEco Studio 的 Profiler 有几个面板很有用:
Performance 面板:看每一帧的耗时,哪一帧掉帧了,红色的条就是超标了。点进去能看到这一帧花了多少时间在布局、渲染、脚本上。
Memory 面板:看滚动的时候内存有没有暴涨。如果越滑内存越高,说明有内存泄漏,大概率是组件没复用或者图片缓存有问题。
App Inspection:能实时看布局层级,有没有不必要的嵌套。
我们当时调优的过程就是:录一段滚动 → 看哪帧掉帧 → 定位是布局还是渲染 → 针对性改代码 → 再录一遍对比。前后大概改了三轮,FPS 从 35 提到了稳定 60。

几个容易踩的坑
1. 不要在 build 里做计算:比如过滤数据、格式化时间,不要写在 build 方法里,每次重绘都会执行。用 @Computed 或者提前算好。
2. 减少布局层级:嵌套三四层 Row/Column 是常事,但层级越多布局开销越大。能用一个容器解决的就不要拆成多个。
3. 避免在 ForEach 里创建对象:比如 ForEach 里 new 一个对象,每次刷新都会创建新的,容易触发不必要的重绘。
小结
长列表优化说到底就是三件事:组件复用、图片优化、局部刷新。听起来都是老生常谈,但真到了项目里,每个细节没做到位,滑起来就是会卡。下一篇讲多设备适配,手机、折叠屏、大屏上怎么一套代码都跑通。
更多推荐



所有评论(0)