文章目录前言第一招ForEach 换 LazyForEach代码实现第二招缩小 item 布局层级核心代码第三招组件复用完整示例第四招图片用缩略图代码解析优化对照表常见误区小白必踩完整可运行示例长列表性能优化实现拆解写在最后前言列表是性能重灾区。一个聊天记录几千条、商品列表无限滚动——如果用错方式轻则滑动卡顿重则内存爆炸直接崩。我做过不少列表优化这篇文章把最实用的几招讲清楚用LazyForEach懒加载、控制单次渲染量、复用组件、减少布局层级。照着做列表丝滑起来。第一招ForEach 换 LazyForEach普通ForEach会一次性把所有 item 都创建出来。1000 条就建 1000 个组件内存直接起飞。LazyForEach只渲染当前屏幕可见 少量缓冲的 item滑到哪建到哪代码实现import{LazyForEach,IDataSource}fromkit.ArkUI// 数据源要实现一个 IDataSource 接口提供数据 通知变化classMyDataSourceimplementsIDataSource{privatelist:Item[][]totalCount():number{returnthis.list.length}getData(index:number):Item{returnthis.list[index]}registerDataChangeListener(_:DataChangeListener):void{}unregisterDataChangeListener(_:DataChangeListener):void{}}build(){List(){LazyForEach(this.dataSource,(item:Item){ListItem(){MyItemView({item:item})}},(item:Item)item.id)// 必须唯一键}.cachedCount(5)// 上下各多缓存 5 个滑动更顺}LazyForEach第一个参数是实现了IDataSource的数据源不是裸数组。.cachedCount(5)屏幕外多预创建 5 个滑动时不用现建减少白屏。唯一键必须有否则报错。经验值列表超过 50 条无脑上LazyForEach。这是长列表优化的第一杠杆。第二招缩小 item 布局层级每个 ListItem 的布局越复杂渲染越慢。能扁平就别嵌套核心代码// 不好套了 4 层容器Column(){Row(){Column(){Row(){Text(x)}}}}// 好直接一层Row(){Image(...).width(40)Text(x).layoutWeight(1).margin({left:8})}减少无意义的Column/Row嵌套少一层少一份测量开销。固定尺寸的元素尽量给具体宽高避免父容器反复测量。第三招组件复用LazyForEach默认会复用滑出屏幕的 ListItem 组件同一类型的不要在每个 item 里new大对象避免重建开销。完整示例ListItem(){MyItemView({item:item})// MyItemView 是复用的自定义组件}把 item 抽成Component自定义组件框架能更好地复用其节点提升滚动流畅度。第四招图片用缩略图长列表里的大图是内存杀手。网络图用缩略图 URL本地图控制尺寸代码解析Image(item.thumbUrl)// 缩略图不是原图.width(80).height(80).objectFit(ImageFit.Cover)千万别在列表里加载原图一张 5MB 原图 × 100 条 500MB必崩。显示用缩略图点进去详情页再加载原图。优化对照表手段解决什么问题效果LazyForEach全量创建组件内存↓ 启动↓cachedCount滑动白屏流畅度↑减层级测量开销渲染↑缩略图大图占内存OOM↓常见误区小白必踩误区说明不用LazyForEachForEach一次性建所有 item几千条直接内存爆炸。超 50 条无脑上 LazyForEach。布局嵌套太深每个 ListItem 套 4 层容器测量开销大。能扁平就扁平。列表用原图一张 5MB 原图 × 100 条 500MB 必崩用缩略图。不设置cachedCount屏幕外多预创建几个滑动才不白屏。下面这段代码可以直接复制到 DevEco Studio 里运行。建议你边读边敲改一改文末「动手改一改」里的参数亲眼看看效果。完整可运行示例长列表性能优化用LazyForEach替代ForEach做万级列表懒加载。实现拆解// LazyForEach只渲染可视区内存恒定import{LazyForEach,IDataSource}fromkit.ArkUIclassNumSourceimplementsIDataSource{privatenums:number[]Array.from({length:10000},(_,i)i)totalCount():number{returnthis.nums.length}getData(index:number):number{returnthis.nums[index]}registerDataChangeListener(){}unregisterDataChangeListener(){}}EntryComponentstruct BigList{privatesource:NumSourcenewNumSource()build(){List(){LazyForEach(this.source,(n:number){ListItem(){Text(行 n).padding(12)}},(n:number)n.toString())}.cachedCount(5)// 预加载可视区外的 5 个滚动更顺}}你会看到什么一万个数据项滚动依然流畅因为框架只创建可视区 预加载的少量ListItem滚出屏幕的组件会被回收。动手改一改把ForEach换成LazyForEach前后各跑一次用 Profiler 看内存差异。调cachedCount大小权衡内存与滚动顺滑度。给ListItem加key稳定标识避免复用错乱。写在最后长列表优化记住四招LazyForEach 懒加载、cachedCount 加缓冲、布局减层级、图片用缩略。其中 LazyForEach 是性价比最高的一招几乎所有长列表卡顿第一步都是换它。回去查你项目里所有ForEach渲染的列表数据量可能上千的立刻换成 LazyForEach——这一改内存和流畅度能同时好一个档次。
HarmonyOS7 长列表性能优化:渲染与内存双提升
发布时间:2026/7/14 18:21:58
文章目录前言第一招ForEach 换 LazyForEach代码实现第二招缩小 item 布局层级核心代码第三招组件复用完整示例第四招图片用缩略图代码解析优化对照表常见误区小白必踩完整可运行示例长列表性能优化实现拆解写在最后前言列表是性能重灾区。一个聊天记录几千条、商品列表无限滚动——如果用错方式轻则滑动卡顿重则内存爆炸直接崩。我做过不少列表优化这篇文章把最实用的几招讲清楚用LazyForEach懒加载、控制单次渲染量、复用组件、减少布局层级。照着做列表丝滑起来。第一招ForEach 换 LazyForEach普通ForEach会一次性把所有 item 都创建出来。1000 条就建 1000 个组件内存直接起飞。LazyForEach只渲染当前屏幕可见 少量缓冲的 item滑到哪建到哪代码实现import{LazyForEach,IDataSource}fromkit.ArkUI// 数据源要实现一个 IDataSource 接口提供数据 通知变化classMyDataSourceimplementsIDataSource{privatelist:Item[][]totalCount():number{returnthis.list.length}getData(index:number):Item{returnthis.list[index]}registerDataChangeListener(_:DataChangeListener):void{}unregisterDataChangeListener(_:DataChangeListener):void{}}build(){List(){LazyForEach(this.dataSource,(item:Item){ListItem(){MyItemView({item:item})}},(item:Item)item.id)// 必须唯一键}.cachedCount(5)// 上下各多缓存 5 个滑动更顺}LazyForEach第一个参数是实现了IDataSource的数据源不是裸数组。.cachedCount(5)屏幕外多预创建 5 个滑动时不用现建减少白屏。唯一键必须有否则报错。经验值列表超过 50 条无脑上LazyForEach。这是长列表优化的第一杠杆。第二招缩小 item 布局层级每个 ListItem 的布局越复杂渲染越慢。能扁平就别嵌套核心代码// 不好套了 4 层容器Column(){Row(){Column(){Row(){Text(x)}}}}// 好直接一层Row(){Image(...).width(40)Text(x).layoutWeight(1).margin({left:8})}减少无意义的Column/Row嵌套少一层少一份测量开销。固定尺寸的元素尽量给具体宽高避免父容器反复测量。第三招组件复用LazyForEach默认会复用滑出屏幕的 ListItem 组件同一类型的不要在每个 item 里new大对象避免重建开销。完整示例ListItem(){MyItemView({item:item})// MyItemView 是复用的自定义组件}把 item 抽成Component自定义组件框架能更好地复用其节点提升滚动流畅度。第四招图片用缩略图长列表里的大图是内存杀手。网络图用缩略图 URL本地图控制尺寸代码解析Image(item.thumbUrl)// 缩略图不是原图.width(80).height(80).objectFit(ImageFit.Cover)千万别在列表里加载原图一张 5MB 原图 × 100 条 500MB必崩。显示用缩略图点进去详情页再加载原图。优化对照表手段解决什么问题效果LazyForEach全量创建组件内存↓ 启动↓cachedCount滑动白屏流畅度↑减层级测量开销渲染↑缩略图大图占内存OOM↓常见误区小白必踩误区说明不用LazyForEachForEach一次性建所有 item几千条直接内存爆炸。超 50 条无脑上 LazyForEach。布局嵌套太深每个 ListItem 套 4 层容器测量开销大。能扁平就扁平。列表用原图一张 5MB 原图 × 100 条 500MB 必崩用缩略图。不设置cachedCount屏幕外多预创建几个滑动才不白屏。下面这段代码可以直接复制到 DevEco Studio 里运行。建议你边读边敲改一改文末「动手改一改」里的参数亲眼看看效果。完整可运行示例长列表性能优化用LazyForEach替代ForEach做万级列表懒加载。实现拆解// LazyForEach只渲染可视区内存恒定import{LazyForEach,IDataSource}fromkit.ArkUIclassNumSourceimplementsIDataSource{privatenums:number[]Array.from({length:10000},(_,i)i)totalCount():number{returnthis.nums.length}getData(index:number):number{returnthis.nums[index]}registerDataChangeListener(){}unregisterDataChangeListener(){}}EntryComponentstruct BigList{privatesource:NumSourcenewNumSource()build(){List(){LazyForEach(this.source,(n:number){ListItem(){Text(行 n).padding(12)}},(n:number)n.toString())}.cachedCount(5)// 预加载可视区外的 5 个滚动更顺}}你会看到什么一万个数据项滚动依然流畅因为框架只创建可视区 预加载的少量ListItem滚出屏幕的组件会被回收。动手改一改把ForEach换成LazyForEach前后各跑一次用 Profiler 看内存差异。调cachedCount大小权衡内存与滚动顺滑度。给ListItem加key稳定标识避免复用错乱。写在最后长列表优化记住四招LazyForEach 懒加载、cachedCount 加缓冲、布局减层级、图片用缩略。其中 LazyForEach 是性价比最高的一招几乎所有长列表卡顿第一步都是换它。回去查你项目里所有ForEach渲染的列表数据量可能上千的立刻换成 LazyForEach——这一改内存和流畅度能同时好一个档次。