长列表性能翻倍:@Reusable 组件复用与复用池实战
·
长列表性能翻倍:@Reusable 组件复用与复用池实战
前言
LazyForEach + cachedCount 解决了“只渲染可视区”的问题(见 9/3 文章),但当列表快速滑动时,频繁创建/销毁自定义组件依然会卡顿、掉帧。鸿蒙提供了 @Reusable 装饰器 + reuseId,让组件像“对象池”一样被回收复用。本文讲清它和 LazyForEach 的配合,以及 @Reusable 下状态初始化的正确姿势。
问题描述
典型症状:
- 长列表快速滑动时明显掉帧,Profiler 显示大量组件创建。
- 给组件加了
@Reusable,结果复用时显示的是上一条的旧数据。 - 组件内部有定时器/订阅,复用时重复创建导致内存泄漏或重复回调。
- 不同“类型”的列表项被错误复用,布局串味。
这些都和“复用时机”与“状态重置”有关。
细节解析
@Reusable 的工作机制:当组件滑出可视区,系统不立即销毁它,而是放进复用池;滑入新位置需要同类型组件时,直接从池里取一个,调用 aboutToReuse(params) 让你更新数据,而不是重新走 aboutToAppear/build。
关键 API:
@Reusable:标记组件可被复用。reuseId:在LazyForEach的itemGenerator里给组件设reuseId(相同reuseId才能互相同复用)。不同类型列表项用不同reuseId,避免错配。aboutToReuse(params):复用时调用,用来把新数据写进状态,替代aboutToAppear里的一次性初始化。aboutToRecycle():即将被回收进池时调用,适合做清理(取消订阅、停定时器)。
与 LazyForEach 的关系:LazyForEach 负责“创建哪些”,@Reusable 负责“创建出来的能否复用”。两者互补,一起上才完整。
高频坑:
- 复用后显示旧数据 → 没在
aboutToReuse里更新状态。 - 复用时定时器叠加 → 没在
aboutToRecycle里清理。 reuseId不设或设错 → 不同类型互相复用导致样式错乱。
示例代码
// 可复用的列表项
@Reusable
@Component
struct NewsItem {
@State title: string = ''
@State desc: string = ''
private timer?: number
aboutToReuse(params: Record<string, Object>) {
// 复用时用新数据覆盖旧状态(关键)
this.title = params.title as string
this.desc = params.desc as string
}
aboutToRecycle() {
// 回收前清理,避免复用后叠加
if (this.timer) { clearInterval(this.timer); this.timer = undefined }
}
aboutToAppear() {
// 仅首次创建时的初始化(复用不会再次进入)
this.timer = setInterval(() => {}, 1000) as unknown as number
}
build() {
Column({ space: 4 }) {
Text(this.title).fontSize(16).fontWeight(FontWeight.Bold)
Text(this.desc).fontSize(13).fontColor('#666')
}
.width('100%')
.padding(12)
.backgroundColor('#fff')
}
}
// 列表页
@Entry
@Component
struct ListPage {
@State data: Array<{ id: number, title: string, desc: string }> = []
build() {
List() {
LazyForEach(this.dataSource, (item) => {
ListItem() {
NewsItem({ title: item.title, desc: item.desc })
.reuseId('news') // 同类型复用标识
}
}, (item) => item.id.toString())
}
.cachedCount(5) // 配合 @Reusable 预渲染缓冲
.width('100%')
.height('100%')
}
}
要点:
reuseId('news')让所有新闻项共享一个复用池;如果有“广告项”用reuseId('ad')区分,互不串。aboutToReuse必须更新全部会变化的@State,否则复用的组件会带着上一条的数据。aboutToRecycle做反向清理,和aboutToAppear成对。
总结
- 长列表卡顿先上
LazyForEach;还不够顺滑就加@Reusable。 @Reusable靠“复用池”省去重复创建,aboutToReuse负责刷新数据,aboutToRecycle负责清理。- 用
reuseId隔离不同类型的列表项,避免错配。 aboutToAppear只做一次性初始化,复用不会重入;状态更新一律放aboutToReuse。- 配合
cachedCount效果最佳。
把“组件复用”和“懒加载”组合起来,长列表性能通常能提升数倍,滑动如丝般顺滑。
更多推荐



所有评论(0)