news 2026/10/2 9:53:36

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

作者头像

张小明

前端开发工程师

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

问题描述

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

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

关键字:List 性能优化、LazyForEach、cachedCount、列表复用

解决方案

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/10/1 14:53:17

GPTBots.ai:从零开始构建企业级AI智能体,无需代码经验

GPTBots.ai是一款企业级AI Agent无代码构建平台&#xff0c;提供Agent、FlowAgent和MultiAgent三种智能体类型&#xff0c;解决LLM幻觉、垂直知识缺乏等痛点。平台支持RAG系统、工作流编排、工具集成&#xff0c;可快速构建企业级AI应用&#xff0c;无需代码经验即可上手&#…

作者头像 李华
网站建设 2026/10/1 19:19:16

洗车行业小程序源码系统一体化智能后台,让管理清晰高效

温馨提示&#xff1a;文末有资源获取方式痛点一&#xff1a;客户来无影去无踪&#xff0c;复购率低&#xff1f; 解决方案&#xff1a;构建私域会员池&#xff0c;实现精准触达。 系统内置完整的会员卡体系。您可以设置不同等级的会员卡&#xff08;如次卡、月卡、年卡&#xf…

作者头像 李华
网站建设 2026/10/2 9:01:09

为什么说PHP程序员一定要学会自我慈悲?

“自我慈悲”不是自我放纵或降低标准&#xff0c;而是在一个高压力、快迭代、强批判的行业里&#xff0c;建立内在的稳定系统和可持续的燃料补给机制。第一部分&#xff1a;为什么PHP程序员是“自我苛责”的重灾区&#xff1f;技术生态的“原罪”与鄙视链压力 PHP长期处于技术圈…

作者头像 李华
网站建设 2026/10/2 7:23:13

Blender终极指南:如何快速导入虚幻引擎PSK和PSA文件

Blender终极指南&#xff1a;如何快速导入虚幻引擎PSK和PSA文件 【免费下载链接】io_scene_psk_psa A Blender plugin for importing and exporting Unreal PSK and PSA files 项目地址: https://gitcode.com/gh_mirrors/io/io_scene_psk_psa 想要在Blender中无缝使用虚…

作者头像 李华
网站建设 2026/10/2 1:13:52

31、深入探索EXT2文件系统:操作、遍历与实现

深入探索EXT2文件系统:操作、遍历与实现 1. 实验操作 在Linux系统中,我们可以进行一系列与EXT2文件系统相关的实验操作。 - 挂载与操作文件系统 :首先,将 mydisk 挂载到Linux系统下。接着,在挂载的文件系统中创建新的目录,并将文件复制到该文件系统中。完成操作后…

作者头像 李华