HarmonyOS 7 瀑布流与长列表性能调优
电商大促活动页是性能的重灾区。几百张尺寸不一的商品图,加上复杂的标签和倒计时,只要手指滑得快一点,画面就疯狂掉帧,甚至出现大面积的白块。为了挽回用户口碑,我花了整整一周时间,对 ArkUI 的列表渲染机制进行了彻底的解剖。
放弃 ForEach,拥抱 LazyForEach
这是最基础的一步,但很多人却做成了假分页。如果你用 ForEach 去渲染 1000 条数据,不管屏幕看不看得见,这 1000 个组件都会被一次性创建并加入渲染树。内存不爆才怪。
必须换成 LazyForEach,它会自动计算可视区域,只渲染屏幕内及缓存区的节点。这就要求你必须实现 IDataSource 接口,并在数据发生变化时,主动调用 onDataAdd 等方法去通知 UI 刷新,而不是简单的重新赋值数组。
组件复用:省下的是 CPU 和内存
仅仅用 LazyForEach 还不够。当用户快速滑动时,旧节点被销毁,新节点被创建,反反复复的 build() 调用依然会卡顿。
加上 @Reusable 装饰器,可以让组件被框架缓存起来。当新的数据进入屏幕时,直接复用旧的组件实例,仅仅更新里面的数据。
typescript
@Reusable
@Component
struct GoodsCard {
@State item: GoodsData = {} as GoodsData;
// 警示:复用时不会走 aboutToAppear,必须在这里更新数据
aboutToReuse(params: Record<string, Object>): void {
this.item = params.data as GoodsData;
}
build() {
// UI结构必须保持一致,不能有破坏复用的 if/else
Column() {
Image(this.item.imageUrl).aspectRatio(1)
Text(this.item.title).maxLines(2)
}
}
}
布局层级的扁平化手术
检查渲染树时,发现一个商品卡片里嵌套了 5 层 Column 和 Row。ArkUI 在每次布局计算(Measure)时,都要自上而下遍历所有的节点。层级越深,计算量越大。
我们大刀阔斧地把所有的多层嵌套改成了 RelativeContainer,或者直接利用 Flex 的弹性特性进行单层布局。把渲染树的深度压缩到了 2 层以内。这一改动,让滑动的帧率直接提升了 15 帧。
图片加载的异步与降采样
列表里全是高分辨率的网络图片。如果在滑动过程中去实时下载解码,绝对会卡死。
一方面,必须依靠 LazyForEach 的懒加载机制;另一方面,必须使用专门的图片缓存库,或者利用鸿蒙的 Image 组件自带的内存缓存。更关键的是,服务端下发的图片 URL,必须带上 CDN 的裁剪参数,只拉取 300x300 的缩略图,绝不能让客户端下载原图后再去压缩。对于本地图片,必须使用 desiredSize 进行解码降采样。
避免 if/else 带来的毁灭性重建
在设计卡片的状态时,比如“已售罄”和“未售罄”,千万不要用 if/else 去控制整个卡片的显示。
每次状态变化,if/else 都会把组件彻底销毁重建。应该用 visibility 属性去隐藏售罄遮罩,或者仅仅在 Text 组件上改变字体的颜色和内容。保持节点结构的稳定性,是长列表流畅的根本保障。
在一个页面里用 LazyForEach 创建 500 条包含图片的列表。加上 @Reusable,并在滑动时观察内存曲线。如果你看到了绿色的内存占用在平稳波动,而不是像心电图一样上下乱跳,说明你的复用策略真正生效了。
更多推荐


所有评论(0)