news 2026/2/23 4:14:18

[鸿蒙2025领航者闯关]List组件性能优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[鸿蒙2025领航者闯关]List组件性能优化

问题描述

在 HarmonyOS 开发中,List 列表是最常用的组件,但数据量大时容易出现性能问题:

  • 滚动卡顿,帧率下降
  • 加载 1000+ 条数据时崩溃
  • 列表项复杂时渲染慢
  • 内存占用过高

关键字:List 性能优化LazyForEachcachedCount列表复用

解决方案

1. 性能优化核心原则

虚拟列表: 只渲染可见区域 懒加载: LazyForEach按需加载 缓存复用: cachedCount缓存列表项 简化渲染: 减少组件层级

2. 完整优化方案

优化前:性能差的写法
// ❌ 性能差:使用ForEach,全量渲染 @Entry @Component struct BadListDemo { @State items: Item[] = []; // 假设有10000条数据 async aboutToAppear(): Promise<void> { // 一次性加载10000条数据 this.items = await loadAllItems(); // ❌ 内存爆炸 } build() { List() { // ❌ ForEach会渲染所有10000个列表项 ForEach(this.items, (item: Item) => { ListItem() { this.buildComplexItem(item); // ❌ 复杂组件 } }) } } @Builder buildComplexItem(item: Item) { Column() { // ❌ 嵌套层级深 Row() { Column() { Image(item.image) .width(80) .height(80); Column() { Text(item.title).fontSize(16); Text(item.desc).fontSize(14); Text(item.time).fontSize(12); } } } } .width('100%') .padding(16) .backgroundColor(Color.White) } }

问题:

  • ForEach 渲染全部数据,内存爆炸
  • 组件层级深,渲染慢
  • 没有缓存,滚动卡顿
优化后:高性能写法
// ✅ 性能优化:使用LazyForEach + 缓存 import { BasicDataSource } from './BasicDataSource'; ​ /** * 数据源实现 */ class ItemDataSource extends BasicDataSource { private items: Item[] = []; totalCount(): number { return this.items.length; } getData(index: number): Item { return this.items[index]; } addData(item: Item): void { this.items.push(item); this.notifyDataAdd(this.items.length - 1); } pushData(data: Item[]): void { this.items.push(...data); this.notifyDataReload(); } } ​ @Entry @Component struct OptimizedListDemo { private dataSource: ItemDataSource = new ItemDataSource(); @State isLoading: boolean = false; async aboutToAppear(): Promise<void> { await this.loadInitialData(); } /** * 分页加载 */ async loadInitialData(): Promise<void> { this.isLoading = true; // ✅ 只加载第一页(20条) const items = await loadItems(0, 20); this.dataSource.pushData(items); this.isLoading = false; } /** * 加载更多 */ async loadMore(): Promise<void> { if (this.isLoading) { return; } this.isLoading = true; const currentCount = this.dataSource.totalCount(); const items = await loadItems(currentCount, 20); this.dataSource.pushData(items); this.isLoading = false; } build() { List({ space: 12 }) { // ✅ 使用LazyForEach,按需渲染 LazyForEach(this.dataSource, (item: Item, index: number) => { ListItem() { this.buildOptimizedItem(item); } }, (item: Item) => item.id.toString()) // ✅ 提供唯一key } .width('100%') .height('100%') .edgeEffect(EdgeEffect.Spring) // ✅ 缓存10个列表项 .cachedCount(10) // ✅ 滚动到底部时加载更多 .onReachEnd(() => { this.loadMore(); }) } /** * 优化后的列表项 */ @Builder buildOptimizedItem(item: Item) { // ✅ 减少嵌套层级 Row({ space: 12 }) { Image(item.image) .width(60) .height(60) .borderRadius(8) .objectFit(ImageFit.Cover); Column({ space: 4 }) { Text(item.title) .fontSize(16) .fontWeight(FontWeight.Medium) .maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis }); Text(item.desc) .fontSize(14) .fontColor('#666666') .maxLines(2) .textOverflow({ overflow: TextOverflow.Ellipsis }); Text(item.time) .fontSize(12) .fontColor('#999999'); } .layoutWeight(1) .alignItems(HorizontalAlign.Start) } .width('100%') .padding(16) .backgroundColor(Color.White) .borderRadius(12) } }
BasicDataSource 基类
/** * LazyForEach数据源基类 */ export class BasicDataSource implements IDataSource { private listeners: DataChangeListener[] = []; totalCount(): number { return 0; } getData(index: number): Object { return {}; } registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { this.listeners.push(listener); } } unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { this.listeners.splice(pos, 1); } } // ✅ 通知数据新增 notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }); } // ✅ 通知数据删除 notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }); } // ✅ 通知数据变化 notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }); } // ✅ 通知数据重载 notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }); } }
下拉刷新 + 上拉加载
@Component export struct RefreshableList { private dataSource: ItemDataSource = new ItemDataSource(); @State isRefreshing: boolean = false; @State isLoadingMore: boolean = false; build() { Refresh({ refreshing: $$this.isRefreshing }) { List({ space: 12 }) { LazyForEach(this.dataSource, (item: Item) => { ListItem() { this.buildListItem(item); } }, (item: Item) => item.id.toString()) // ✅ 加载更多指示器 if (this.isLoadingMore) { ListItem() { Row() { LoadingProgress() .width(30) .height(30); Text('加载中...') .fontSize(14) .fontColor('#999999') .margin({ left: 8 }); } .width('100%') .height(60) .justifyContent(FlexAlign.Center) } } } .width('100%') .height('100%') .cachedCount(10) .onReachEnd(() => { this.loadMore(); }) } .onRefreshing(() => { this.refresh(); }) } /** * 下拉刷新 */ async refresh(): Promise<void> { // 加载最新数据 const items = await loadItems(0, 20); // 清空旧数据 this.dataSource = new ItemDataSource(); this.dataSource.pushData(items); this.isRefreshing = false; } /** * 上拉加载 */ async loadMore(): Promise<void> { if (this.isLoadingMore) { return; } this.isLoadingMore = true; const currentCount = this.dataSource.totalCount(); const items = await loadItems(currentCount, 20); if (items.length > 0) { this.dataSource.pushData(items); } this.isLoadingMore = false; } @Builder buildListItem(item: Item) { Row() { Text(item.title).fontSize(16); } .width('100%') .padding(16) .backgroundColor(Color.White) } }

关键优化点

1. LazyForEach vs ForEach

ForEachLazyForEach
渲染时机全量渲染按需渲染
内存占用
适用场景<100 条>100 条
性能

2. cachedCount 缓存

List() { LazyForEach(dataSource, ...) } .cachedCount(10) // ✅ 缓存10个列表项 ​ // 工作原理: // 可见5个 + 上方缓存5个 + 下方缓存5个 = 总共15个

3. 提供唯一 key

// ✅ 正确:提供唯一key LazyForEach(dataSource, (item: Item) => { ListItem() { } }, (item: Item) => item.id.toString()) // 唯一key ​ // ❌ 错误:使用index作为key }, (item: Item, index: number) => index.toString()) // 数据顺序变化会出错

4. 减少组件层级

// ❌ 层级深(6层) Column() { Row() { Column() { Row() { Column() { Text('内容') // 第6层 } } } } } ​ // ✅ 层级浅(2层) Row() { Text('内容') // 第2层 }

最佳实践

1. 分页加载

class PaginatedDataSource extends BasicDataSource { private items: Item[] = []; private pageSize: number = 20; private currentPage: number = 0; private hasMore: boolean = true; async loadNextPage(): Promise<void> { if (!this.hasMore) { return; } const items = await loadItems(this.currentPage, this.pageSize); if (items.length < this.pageSize) { this.hasMore = false; } this.items.push(...items); this.currentPage++; this.notifyDataReload(); } }

2. 图片懒加载

@Builder buildListItem(item: Item) { Row() { // ✅ 图片设置合适大小,避免内存浪费 Image(item.image) .width(60) .height(60) .objectFit(ImageFit.Cover) .interpolation(ImageInterpolation.Low) // 低质量插值 } }

3. 复杂列表项优化

// ✅ 使用@Reusable实现组件复用 @Reusable @Component struct ReusableListItem { @State item: Item | null = null; // ✅ aboutToReuse在复用时调用 aboutToReuse(params: Record<string, Object>): void { this.item = params.item as Item; } build() { if (this.item) { Row() { Text(this.item.title); } } } }

常见问题

Q1: 数据更新后列表不刷新?

// ❌ 错误:直接修改数组 this.items[0].title = 'new'; // UI不更新 // ✅ 正确:通知数据源 this.dataSource.notifyDataChange(0); // 通知第0项变化

Q2: 如何实现列表项删除动画?

ListItem() { this.buildListItem(item); } // ✅ 添加删除动画 .transition(TransitionEffect.OPACITY .animation({ duration: 300 }) .combine(TransitionEffect.translate({ x: -100 })) )

Q3: 如何监控列表性能?

List() { LazyForEach(dataSource, ...) } .onScrollIndex((start, end) => { console.info(`可见范围: ${start} - ${end}`); }) .onScroll((scrollOffset, scrollState) => { if (scrollState === ScrollState.Fling) { console.warn('快速滚动中'); } })

性能对比

测试场景: 10000 条数据

方案初始加载时间内存占用滚动帧率
ForEach~8s~500MB20fps
LazyForEach~0.3s~50MB60fps
LazyForEach+cachedCount~0.2s~60MB60fps

结论: LazyForEach 性能提升40 倍!

总结

LazyForEach: 按需渲染,内存占用低 ✅cachedCount: 缓存列表项,滚动流畅 ✅唯一 key: 正确复用组件 ✅分页加载: 避免一次加载过多 ✅减少层级: 简化组件结构 ✅@Reusable: 组件复用优化

掌握这些技巧,可以轻松处理万级数据列表!

参考资料

  • List 组件开发指南
  • LazyForEach 性能优化
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/22 23:47:19

人形机器人关节执行器__轻量化摆线减速器PEEK精密注塑降本方案

人形机器人的规模化落地&#xff0c;对关节核心传动部件提出了轻量化与低成本的双重挑战。以精密注塑工艺&#xff0c;将高性能特种工程塑料一体成型为摆线减速器的技术方案&#xff0c;正成为突破传统金属方案局限、开启产业化新路径的关键。这不仅实现了部件性能的跃升&#…

作者头像 李华
网站建设 2026/2/18 10:48:25

[特殊字符]《Shell 编程没那么难!给完全新手的温柔入门指南》

适合谁&#xff1f;第一次听说 “Shell 脚本” 的你看到 #!/bin/bash 就头大的你觉得“命令行好可怕”的你只想“让电脑帮我干点重复活”的实用派读完你能做到&#xff1a; ✅ 理解 Shell 到底是什么 ✅ 写出第一个能运行的脚本 ✅ 修改现成脚本为自己所用 ✅ 不再害怕那些奇怪…

作者头像 李华
网站建设 2026/2/20 21:56:14

5大核心功能解析:如何用云端技术重塑情侣互动体验

5大核心功能解析&#xff1a;如何用云端技术重塑情侣互动体验 【免费下载链接】Rainbow-Cats-Personal-WeChat-MiniProgram 给女朋友做的微信小程序&#xff01;情侣自己的任务和商城系统&#xff01; 项目地址: https://gitcode.com/gh_mirrors/ra/Rainbow-Cats-Personal-We…

作者头像 李华
网站建设 2026/2/17 7:57:48

洗车小程序源码系统,打造你自己的“洗车服务平台”

温馨提示&#xff1a;文末有资源获取方式春哥洗车小程序V4.2的核心定位非常清晰&#xff1a;“既能适用于平台&#xff0c;又能用作单锁门店”。这意味着一套系统&#xff0c;两种商业模式&#xff0c;无限可能。源码获取方式在源码闪购网。对于野心勃勃的创业者或企业&#xf…

作者头像 李华
网站建设 2026/2/14 15:09:20

SYS-2722音频分析仪 SYS-2722 Audio Precision

SYS-2722音频分析仪Audio PrecisionSYS-2722是一台通过电脑控制的多功能音频综合测试系统&#xff0c;它主要由模拟信号源、模拟分析仪、数字信号源、数字分析仪、数字接口信号源及数字接口分析仪等部分组成。其测试的模拟参数有&#xff1a;频率、电平、失真、信噪比、相位、带…

作者头像 李华