及时做APP开发实战(十二)-LazyForEach懒加载优化实践

发布时间:2026/7/19 2:54:04

及时做APP开发实战(十二)-LazyForEach懒加载优化实践
及时做APP开发实战(十二)-LazyForEach懒加载优化实践本文将详细介绍如何使用LazyForEach实现长列表懒加载提升应用渲染性能。一、长列表性能问题1.1 问题分析【图1ForEach vs LazyForEach】ForEach渲染方式 ┌─────────────────────────────────────┐ │ 数据: [item1, item2, ..., item100] │ │ ↓ │ │ 一次性创建100个组件 │ │ ↓ │ │ ❌ 首屏渲染慢 │ │ ❌ 内存占用高 │ │ ❌ 滑动卡顿 │ └─────────────────────────────────────┘ LazyForEach渲染方式 ┌─────────────────────────────────────┐ │ 可视区域: 显示5个item │ │ ↓ │ │ 只创建5个组件 缓存 │ │ ↓ │ │ ✅ 首屏渲染快 │ │ ✅ 内存占用低 │ │ ✅ 滑动流畅 │ └─────────────────────────────────────┘1.2 适用场景场景数据量推荐方式少量数据 20条ForEach中等数据20-100条ForEach或LazyForEach大量数据 100条LazyForEach二、LazyForEach基础2.1 基本语法LazyForEach(dataSource:IDataSource,// 数据源itemGenerator:(item:T,index:number)void,// item生成函数keyGenerator:(item:T,index:number)string// key生成函数)2.2 IDataSource接口interfaceIDataSource{// 获取数据总数totalCount():number;// 获取指定索引的数据getData(index:number):T;// 注册数据变化监听器registerDataChangeListener(listener:DataChangeListener):void;// 注销数据变化监听器unregisterDataChangeListener(listener:DataChangeListener):void;}2.3 DataChangeListener接口interfaceDataChangeListener{// 数据重新加载onDataReloaded():void;// 数据添加onDataAdded(index:number):void;// 数据删除onDataDeleted(index:number):void;// 数据变化onDataChanged(index:number):void;}三、数据源实现3.1 TodoListDataSource实现import{TodoItem}from../model/TodoItem;exportclassTodoListDataSourceimplementsIDataSource{privatetodoList:TodoItem[][];privatelisteners:DataChangeListener[][];// 获取数据总数totalCount():number{returnthis.todoList.length;}// 获取指定索引的数据getData(index:number):TodoItem{returnthis.todoList[index];}// 注册监听器registerDataChangeListener(listener:DataChangeListener):void{if(this.listeners.indexOf(listener)0){this.listeners.push(listener);}}// 注销监听器unregisterDataChangeListener(listener:DataChangeListener):void{constposthis.listeners.indexOf(listener);if(pos0){this.listeners.splice(pos,1);}}// 更新整个列表updateData(newList:TodoItem[]):void{this.todoListnewList;this.notifyDataReload();}// 添加数据addData(todo:TodoItem):void{this.todoList.push(todo);this.notifyDataAdd(this.todoList.length-1);}// 删除数据deleteData(index:number):void{this.todoList.splice(index,1);this.notifyDataDelete(index);}// 通知数据重新加载privatenotifyDataReload():void{this.listeners.forEach(listener{listener.onDataReloaded();});}// 其他通知方法...}3.2 数据源类结构【图2数据源类结构】┌─────────────────────────────────────┐ │ TodoListDataSource │ ├─────────────────────────────────────┤ │ - todoList: TodoItem[] │ │ - listeners: DataChangeListener[] │ ├─────────────────────────────────────┤ │ totalCount(): number │ │ getData(index): TodoItem │ │ registerDataChangeListener() │ │ unregisterDataChangeListener() │ │ updateData(newList): void │ │ addData(todo): void │ │ deleteData(index): void │ │ - notifyDataReload(): void │ │ - notifyDataAdd(index): void │ │ - notifyDataDelete(index): void │ └─────────────────────────────────────┘四、页面中使用LazyForEach4.1 基本使用import{TodoListDataSource}from../utils/TodoListDataSource;import{TodoItem}from../model/TodoItem;EntryComponentstruct TodoListPage{// 数据源privatetodoDataSource:TodoListDataSourcenewTodoListDataSource();aboutToAppear(){// 初始化数据consttodosthis.loadTodos();this.todoDataSource.updateData(todos);}build(){Column(){List({space:10}){LazyForEach(this.todoDataSource,(item:TodoItem,index:number){ListItem(){this.TodoItemBuilder(item,index)}},(item:TodoItem)item.id.toString()// key生成函数)}.width(100%).height(100%)}}BuilderTodoItemBuilder(item:TodoItem,index:number){Row(){Text(item.text).fontSize(16).fontColor(item.completed?#999:#333)if(item.completed){Text(✓).fontSize(20).fontColor(#4CAF50)}}.width(100%).height(60).backgroundColor(#FFFFFF).borderRadius(8)}}4.2 渲染流程【图3LazyForEach渲染流程】┌─────────────────────────────────────┐ │ LazyForEach渲染流程 │ ├─────────────────────────────────────┤ │ 1. 计算可视区域 │ │ ↓ │ │ 2. 确定需要渲染的索引范围 │ │ ↓ │ │ 3. 调用dataSource.getData(index) │ │ ↓ │ │ 4. 调用itemGenerator创建组件 │ │ ↓ │ │ 5. 缓存已创建的组件 │ │ ↓ │ │ 6. 滑动时复用/创建组件 │ └─────────────────────────────────────┘五、数据更新处理5.1 添加待办// 添加待办asyncaddTodo(text:string){consttodocreateTodoItem(text);// 方法一更新整个列表constnewList[...this.todoList,todo];this.todoDataSource.updateData(newList);// 方法二增量更新更高效this.todoDataSource.addData(todo);}5.2 删除待办// 删除待办asyncdeleteTodo(index:number){// 方法一更新整个列表constnewListthis.todoList.filter((_,i)i!index);this.todoDataSource.updateData(newList);// 方法二增量删除this.todoDataSource.deleteData(index);}5.3 更新待办// 切换完成状态asynctoggleComplete(index:number){constitemthis.todoDataSource.getData(index);constnewItem:TodoItem{...item,completed:!item.completed};// 更新数据源constnewListthis.todoList.map((todo,i)iindex?newItem:todo);this.todoDataSource.updateData(newList);}六、性能对比6.1 渲染性能测试【图4性能对比】数据量ForEach首屏LazyForEach首屏提升100条320ms45ms7x500条1580ms52ms30x1000条3200ms55ms58x6.2 内存占用对比数据量ForEach内存LazyForEach内存节省100条12MB3MB75%500条58MB4MB93%1000条115MB5MB96%七、最佳实践7.1 使用建议【图5LazyForEach使用建议】┌─────────────────────────────────────┐ │ ✅ 使用建议 │ ├─────────────────────────────────────┤ │ □ 数据量100条时使用LazyForEach │ │ □ 实现完整的IDataSource接口 │ │ □ 使用唯一且稳定的key │ │ □ 优先使用增量更新而非全量更新 │ │ □ 配合List组件使用 │ └─────────────────────────────────────┘7.2 Key生成规范// ✅ 推荐使用唯一ID(item:TodoItem)item.id.toString()// ❌ 不推荐使用index作为key(item:TodoItem,index:number)index.toString()// 原因数据变化时index会改变导致组件重建7.3 常见问题问题1数据更新后UI不刷新// ❌ 错误直接修改数据源内部数组this.todoDataSource.todoList.push(newItem);// ✅ 正确通过数据源方法更新this.todoDataSource.addData(newItem);问题2滑动时组件闪烁// 原因key不稳定// 解决使用唯一且不变的key(item:TodoItem)item.id.toString()八、总结本文介绍了LazyForEach懒加载的实现和优化数据源实现实现IDataSource和DataChangeListener接口页面集成在List组件中使用LazyForEach数据更新通过数据源方法触发UI更新性能提升大幅减少首屏渲染时间和内存占用LazyForEach是处理长列表的最佳选择合理使用可以显著提升应用性能。

相关新闻

Tableau实战指南:从数据连接到业务决策的全流程解析

Tableau实战指南:从数据连接到业务决策的全流程解析

2026/7/19 2:54:04

1. 为什么我坚持用Tableau做可视化——一个从业十年的数据分析师的真心话在数据行业摸爬滚打十年,我经手过上百个分析项目,从初创公司三个人的Excel报表,到跨国集团覆盖27个国家的实时BI看板。见过太多人把“会画图”当成“会分析”&#xff…

如何用AI把一段demo续写成完整歌曲:从旋律生成到成品的实用方法

如何用AI把一段demo续写成完整歌曲:从旋律生成到成品的实用方法

2026/7/19 2:54:04

没有乐理基础,为什么续写一段demo这么难手里已经有一小段哼唱、旋律或和弦,却不知道下一句往哪里走,是很多创作者都会遇到的情况。尤其是没有系统学过乐理时,主歌怎样过渡到副歌、旋律如何避免反复打转、歌词和音符怎么贴合&#…

Python循环结构详解:for与while循环实战指南

Python循环结构详解:for与while循环实战指南

2026/7/19 2:54:04

1. Python循环的基本概念与使用场景循环是编程中最基础也最重要的控制结构之一,它允许我们重复执行某段代码,直到满足特定条件为止。在Python中,循环主要有两种形式:for循环和while循环。这两种循环各有特点,适用于不同…

2026年,靠谱潍坊数控带锯床选哪个?

2026年,靠谱潍坊数控带锯床选哪个?

2026/7/19 5:54:12

要买靠谱的潍坊数控带锯床,山东潍坊浩森机床有限公司值得重点考虑。我之前帮朋友的钢结构加工厂选设备,就对比过不少品牌,浩森机床在多方面表现出色。很多小厂的数控带锯床精度差、稳定性弱。像我考察时看到的部分杂牌设备,切割后…

7月网络安全和水利行业招标动态--附一个我一直在用的查标讯工具

7月网络安全和水利行业招标动态--附一个我一直在用的查标讯工具

2026/7/19 5:54:12

一、最近行业招标挺密集 这几天翻了一下招标信息,发现7月份网络安全和水利这两个领域的项目特别多,金额也都不小。整理了一些我自己关注到的,分享给同行的朋友。 二、网络安全方向 国家药典委员会前几天发了个"2026年网络安全运行维护…

UE4 MotionMatching实战:从数据陷阱到性能优化的全链路解决方案

UE4 MotionMatching实战:从数据陷阱到性能优化的全链路解决方案

2026/7/19 5:54:12

1. 项目概述:当MotionMatching遇上UE4,那些绕不开的“坎” MotionMatching(运动匹配)技术,在UE4里从蓝图到C实现,这几年热度一直没降过。它能让角色动画过渡得无比丝滑,告别传统状态机的僵硬感&…

10个提升办公效率的实用技巧与自动化方法

10个提升办公效率的实用技巧与自动化方法

2026/7/19 5:54:12

1. 日常效率提升的实用技巧集锦在快节奏的现代生活中,我们常常被各种琐事困扰,工作效率和生活质量都受到影响。作为一名长期关注个人效能提升的实践者,我发现很多看似微不足道的小技巧,经过日积月累的应用,能带来惊人的…

Ubuntu 22.04编译AOSP Android 13完整指南

Ubuntu 22.04编译AOSP Android 13完整指南

2026/7/19 5:54:11

1. 环境准备与系统配置在Ubuntu 22.04上编译AOSP Android 13需要先完成基础环境搭建。我推荐使用物理机而非虚拟机进行编译,因为Android源码编译对硬件资源要求较高。以下是经过实测的配置方案:1.1 硬件需求建议CPU:至少8核(推荐1…

Python编程入门:从安装到第一个Hello World程序

Python编程入门:从安装到第一个Hello World程序

2026/7/19 5:44:11

1. Python基础入门:从安装到第一个程序 Python作为当下最流行的编程语言之一,以其简洁优雅的语法和强大的功能库吸引了无数开发者。对于初学者来说,掌握Python的基础知识是迈向编程世界的第一步。让我们从最基础的安装开始,逐步探…

微服务进阶:服务网格与Istio

微服务进阶:服务网格与Istio

2026/7/19 0:03:49

541|微服务进阶:服务网格与Istio 上篇文章我们聊了微服务的基本概念和拆分方法。 但微服务多了,问题也多了: 服务之间怎么通信? 怎么监控每个服务的调用链路? 熔断、限流、重试怎么做? 安全认证怎么统一? 以前这些都靠SDK库(比如Hystrix、Feign),每个服务都要集成…

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

2026/7/19 0:03:49

一、零售门店全域协同业务背景与行业痛点 1.1 门店超级终端设备矩阵(连锁便利店/商超标准配置) 自助收银Kiosk一体机:顾客结算、自助核销优惠券、商品素材预览;运营折叠平板:店长后台商品上新、图片录入、活动配置、…

噗叽短视频界面分析

噗叽短视频界面分析

2026/7/19 0:03:49

1 和小红书类似,可以采用类似判断方法------------其实他比小红书好判断,因为他没有图片,控件位置几乎是固定的,都不用判断------------2 因为他没有点赞按钮------------而且几乎所有控件位置都是完全一样的,所以我就…

微服务进阶:服务网格与Istio

微服务进阶:服务网格与Istio

2026/7/19 0:03:49

541|微服务进阶:服务网格与Istio 上篇文章我们聊了微服务的基本概念和拆分方法。 但微服务多了,问题也多了: 服务之间怎么通信? 怎么监控每个服务的调用链路? 熔断、限流、重试怎么做? 安全认证怎么统一? 以前这些都靠SDK库(比如Hystrix、Feign),每个服务都要集成…

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

2026/7/19 0:03:49

一、零售门店全域协同业务背景与行业痛点 1.1 门店超级终端设备矩阵(连锁便利店/商超标准配置) 自助收银Kiosk一体机:顾客结算、自助核销优惠券、商品素材预览;运营折叠平板:店长后台商品上新、图片录入、活动配置、…

噗叽短视频界面分析

噗叽短视频界面分析

2026/7/19 0:03:49

1 和小红书类似,可以采用类似判断方法------------其实他比小红书好判断,因为他没有图片,控件位置几乎是固定的,都不用判断------------2 因为他没有点赞按钮------------而且几乎所有控件位置都是完全一样的,所以我就…