
文章目录前言一、列表性能ForEach vs LazyForEach1.1 ForEach 的局限性1.2 LazyForEach虚拟化渲染懒加载二、State 粒度控制避免不必要的重渲染2.1 粗粒度 State 的问题2.2 细粒度 State只有相关组件重渲染三、避免在 build() 中做计算3.1 错误build() 内部大量计算3.2 正确将计算结果存入 State四、图片优化4.1 使用合适的图片格式4.2 图片懒加载五、异步优化不阻塞 UI 线程5.1 长任务异步化5.2 防抖与节流六、性能检测与调优6.1 使用 Profiler 定位性能瓶颈6.2 本项目可优化点总结前言一个 App 功能完善但卡顿迟钝用户体验同样很差。性能优化是从能用到好用的必经之路。本篇聚焦 HarmonyOS ArkUI 的性能关键点列表渲染、状态粒度控制、异步处理和图片优化结合项目实际代码讲解优化思路。一、列表性能ForEach vs LazyForEach1.1 ForEach 的局限性// ❌ 普通 ForEach全量渲染数据多时性能差List(){ForEach(this.thousandItems,(item:string){ListItem(){Text(item).padding(16)}})}// 问题1000条数据 → 一次创建 1000 个节点 → 内存占用高首次渲染慢1.2 LazyForEach虚拟化渲染懒加载// ✅ LazyForEach只渲染可见区域的节点滚动时复用// 首先定义数据源实现 IDataSource 接口classStationDataSourceimplementsIDataSource{privatestations:string[];privatelisteners:DataChangeListener[][];constructor(data:string[]){this.stationsdata;}totalCount():number{returnthis.stations.length;}getData(index:number):string{returnthis.stations[index];}// 以下方法用于通知数据变化registerDataChangeListener(listener:DataChangeListener):void{this.listeners.push(listener);}unregisterDataChangeListener(listener:DataChangeListener):void{constfiltered:DataChangeListener[][];for(leti:number0;ithis.listeners.length;i){if(this.listeners[i]!listener){filtered.push(this.listeners[i]);}}this.listenersfiltered;}notifyDataAdd(index:number):void{this.listeners.forEach(ll.onDataAdd(index));}notifyDataDelete(index:number):void{this.listeners.forEach(ll.onDataDelete(index));}}// 生成 1000 条模拟数据functionbuildStationArray():string[]{constresult:string[][];for(leti:number0;i1000;i){result.push(加油站${i1});}returnresult;}EntryComponentstruct LazyForEachDemo{// 生成1000条数据privatedataSource:StationDataSourcenewStationDataSource(buildStationArray());build(){List(){LazyForEach(this.dataSource,// 数据源(item:string){// 渲染函数ListItem(){Row({space:12}){Text(⛽).fontSize(20)Text(item).fontSize(15)}.padding(16).width(100%).backgroundColor(#FFFFFF).margin({bottom:8})}},(item:string)item// key 生成函数必须唯一且稳定)}.cachedCount(5)// 可见区域外预缓存5个节点.width(100%).height(100%)}}性能对比方案1000条数据首次渲染内存占用滚动流畅度ForEach慢全量创建高取决于数量LazyForEach快只创建可见项低始终流畅二、State 粒度控制避免不必要的重渲染2.1 粗粒度 State 的问题// ❌ 问题一个大对象的任何属性变化都会触发整个组件树重渲染StatepageData:{stations:GasStation[];selectedId:string;sortOrder:string;filterBrand:string;mapCenter:{lat:number,lng:number};isLoading:boolean;errorMsg:string;}{/* ... */};2.2 细粒度 State只有相关组件重渲染// ✅ 每个状态独立只影响使用该状态的组件Statestations:GasStation[][];// 只有列表用StateselectedId:string;// 只有选中态用StatesortOrder:stringdistance;// 只有排序栏用StatefilterBrand:string全部;// 只有筛选栏用StateisLoading:booleanfalse;// 只有 Loading 组件用StateerrorMsg:string;// 只有错误视图用// 修改 selectedId 时只有使用 selectedId 的组件重渲染// 不影响 stations 列表的渲染三、避免在 build() 中做计算3.1 错误build() 内部大量计算// ❌ 每次渲染都重新执行这些计算build(){Column(){// 每次 build 都执行排序 过滤List(){ForEach(this.stations.filter(ss.brandthis.filterBrand).sort((a,b)a.distance-b.distance),(s:GasStation){ListItem(){/* ... */}})}}}3.2 正确将计算结果存入 State// ✅ 计算结果缓存到状态build() 只读取状态StatefilteredStations:GasStation[][];// 在数据变化时Watch 或 onClick 中重新计算StateWatch(recompute)filterBrand:string全部;StateWatch(recompute)sortOrder:stringdistance;recompute():void{// 只在数据或条件真正变化时计算letresultthis.stations;if(this.filterBrand!全部){resultresult.filter(ss.brandthis.filterBrand);}result[...result].sort((a,b)a.distance-b.distance);this.filteredStationsresult;}build(){Column(){// build 中只读取预计算的结果List(){ForEach(this.filteredStations,(s:GasStation){ListItem(){/* ... */}})}}}四、图片优化4.1 使用合适的图片格式// SVG矢量图适合图标Image($r(app.media.station))// station.svg本项目// WebP适合照片/复杂图片比 PNG 小约 30%// PNG适合需要透明背景的图片// JPEG不支持透明适合照片最小// 设置图片尺寸避免加载超大图然后缩小显示Image($r(app.media.banner)).width(200).height(100).objectFit(ImageFit.Cover)// 裁剪以填满容器4.2 图片懒加载Image($r(app.media.station)).width(48).height(48)// 设置占位图图片未加载完成时显示.alt($r(app.media.placeholder))// 图片加载完成回调.onComplete((event){if(event){console.log(图片加载完成${event.width}x${event.height});}}).onError((){console.error(图片加载失败);})五、异步优化不阻塞 UI 线程5.1 长任务异步化// ❌ 错误同步执行耗时操作阻塞 UIonClick(){// 假设这个操作需要200ms如大量数据处理constresultheavyCalculation(this.data);this.displayDataresult;}// ✅ 正确使用 async/await 或 setTimeout 让 UI 先响应onClick(){this.isProcessingtrue;// 先显示 Loading// 让 UI 先更新再执行耗时操作setTimeout(async(){constresultawaitheavyCalculationAsync(this.data);this.displayDataresult;this.isProcessingfalse;},0);}5.2 防抖与节流classPerformanceUtil{// 防抖在最后一次调用后 delay ms 执行staticdebounceTextendsunknown[](fn:(...args:T)void,delay:number):(...args:T)void{lettimer:number-1;return(...args:T){clearTimeout(timer);timersetTimeout(()fn(...args),delay);};}// 节流每 interval ms 最多执行一次staticthrottleTextendsunknown[](fn:(...args:T)void,interval:number):(...args:T)void{letlastTime:number0;return(...args:T){constnowDate.now();if(now-lastTimeinterval){lastTimenow;fn(...args);}};}}// 使用搜索框防抖减少接口调用privatedebouncedSearchPerformanceUtil.debounce((keyword:string){this.fetchSearchResults(keyword);},300);// 使用地图 onScroll 节流减少计算频率privatethrottledOnScrollPerformanceUtil.throttle((y:number){this.scrollYy;this.showBackTopy300;},100);六、性能检测与调优6.1 使用 Profiler 定位性能瓶颈在 DevEco Studio 中运行应用 →View→Performance Profiler选择Frame分析找出掉帧的原因关注CPU Time和JS Heap的峰值6.2 本项目可优化点// 优化1GasStationPage 中调用了两次 getMyLocation重复定位// ❌ 当前代码this.currentLatitude(awaitmapUtil.getMyLocation()).latitude;this.currentLongitude(awaitmapUtil.getMyLocation()).longitude;// ✅ 优化只调用一次constmyLocationawaitmapUtil.getMyLocation();this.currentLatitudemyLocation.latitude;this.currentLongitudemyLocation.longitude;// 优化2ForEach 的 key 函数// ✅ 项目中已正确实现ForEach(this.stationInfoList,(station:StationData){/* ... */},(station:StationData){returnstation.idstation.name;// ← 稳定的 key避免不必要的重渲染});总结HarmonyOS ArkUI 性能优化的核心原则①大列表用LazyForEach替代ForEach②精细化State粒度避免牵一发动全身③不在build()中做计算提前缓存到状态变量④异步执行耗时操作保持 UI 线程响应⑤防抖节流控制高频事件的处理频率。性能优化是一个持续迭代的过程先通过 Profiler 找到瓶颈再针对性优化避免过早优化。