鸿蒙原生APP性能优化之懒加载优化

莱莱  金牌会员 | 2025-2-16 04:16:39 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 876|帖子 876|积分 2628

往期推文全新看点



  • 鸿蒙(HarmonyOS)北向开发知识点记载~
  • 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
  • 鸿蒙应用开发与鸿蒙系统开发哪个更有远景?
  • 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就相识了~
  • 对于大前端开发来说,转鸿蒙开发毕竟是福还是祸?
  • 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
  • 记载一场鸿蒙开发岗位面试履历~
  • 持续更新中……
简介

懒加载LazyForEach是一种耽误加载的技术,它是在必要的时间才加载数据或资源,并在每次迭代过程中创建相应的组件,而不是一次性将全部内容都加载出来。懒加载通常应用于长列表、网格、瀑布流等数据量较大、子组件可重复使用的场景,当用户滚动页面到相应位置时,才会触发资源的加载,以淘汰组件的加载时间,提高应用性能,提拔用户体验。
懒加载原理介绍

渲染过程

在声明式描述语句中,有两种方式控制列表、网格等容器类组件的渲染,分别为循环渲染(ForEach)和数据懒加载(LazyForEach)。


  • 循环渲染
ForEach循环渲染的过程如下:
1. 从列表数据源一次性加载全量数据。
2. 为列表数据的每一个元素都创建对应的组件,并全部挂载在组件树上。即,ForEach遍历多少个列表元素,就创建多少个ListItem组件节点并依次挂载在List组件树根节点上。
3. 列表内容显示时,只渲染屏幕可视区内的ListItem组件,可视区外的ListItem组件滑动进入屏幕内时,由于已经完成了数据加载和组件创建挂载,直接渲染即可。
其数据加载、组件树挂载、页面渲染的示意图如下所示:
图1ForEach渲染过程示意图

如果列表数据较少,数据一次性全量加载不是性能瓶颈时,可以直接使用ForEach;但是当数据量大、组件结构复杂的情况下ForEach会出现性能瓶颈。这是由于要一次性加载全部的列表数据,创建全部组件节点并完成组件树的构建,在数据量大时会非常耗时,从而导致页面启动时间过长。另外,屏幕可视区外的组件固然不会显示在屏幕上,但是仍旧会占用内存。在系统处于高负载的情况下,更容易出现性能问题,极限情况下甚至会导致应用异常退出。


  • 数据懒加载
LazyForEach懒加载的原理和渲染过程如下:
1. LazyForEach会根据屏幕可视区能够容纳显示的组件数量按需加载数据。
2. 根据加载的数据量创建组件,挂载在组件树上,构建出一棵短小的组件树。即,屏幕可以展示多少列表项组件,就按需创建多少个ListItem组件节点挂载在List组件树根节点上。
3. 屏幕可视区只展示部门组件。当可视区外的组件必要在屏幕内显示时,必要从头完成数据加载、组件创建、挂载组件树这一过程,直至渲染到屏幕上。
其数据加载、组件树挂载、页面渲染的示意图如下所示:
图2LazyForEach渲染过程示意图

LazyForEach实现了按需加载,针对列表数据量大、列表组件复杂的场景,淘汰了页面初次启动时一次性加载数据的时间消耗,淘汰了内存峰值。不外在长列表滑动的过程中,由于必要根据用户的滑动行为不停地加载新的内容,这必要举行额外的数据请求和处置惩罚,会增加滑动时的盘算量,从而对性能产生肯定的影响。然而,公道使用LazyForEach的按需加载能力,通过在滑动停止或达到某个阈值时才举行加载,可以淘汰不必要的盘算和请求,从而提高性能,给用户带来更好的体验。总之,在实现按需加载的场景中,必要综合考虑性能和用户体验的平衡,公道地优化加载逻辑和渲染方式,以提拔团体的性能体现。
键值生成规则和组件创建规则

在使用LazyForEach时,我们必要分别实现数据源dataSource、键值生成函数keyGenerator、子组件生成函数itemGenerator。此中,数据源为 IDataSource 范例,必要开发者实现该接口。keyGenerator是一个函数,用于为每个item生成一个唯一且持久的键值以标识对应的组件,开发者可以通过它自界说键值的生成规则,关于键值生成规则。LazyForEach的itemGenerator函数会根据键值生成规则为数据源的每个数组项创建组件,组件的创建分为初次渲染和非初次渲染两种情况。
   说明
在使用LazyForEach举行组件复用时,键值生成函数keyGenerator中不保举使用stringify。在复杂的业务场景中,使用stringify会对item对象举行序列化,终极把item转换成字符串,这过程必要消耗大量的时间和盘算资源,从而导致页面性能低落。
  常见使用场景

LazyForEach作为常见的渲染控制的方式之一,常用的使用场景有长列表加载、无穷瀑布流等。
长列表加载

长列表作为应用开发中最常见的开发场景之一,通常会包含成千上万个列表项,在此场景下,直接使用循环渲染ForEach一次性加载全部的列表项,会导致渲染时间过长,影响用户体验。而使用数据懒加载LazyForEach更换循环渲染ForEach,可以按需加载列表项,从而提拔列表性能。
固然,按需加载列表项可以优化长列表性能,但在快速滑动长列表的场景下,大概会来不及加载必要显示的列表项,导致出现白块的征象,从而影响用户体验。而在ArkUI中,List容器提供了cachedCount属性,LazyForEach可以联合cachedCount属性一起使用,能够避免白块的征象。cachedCount可以设置列表中ListItem/ListItemGroup的预加载数量,并且只在LazyForEach中生效,即cachedCount只能与LazyForEach一起使用。除了List容器,其他容器Grid、Swiper以及WaterFlow也都包含cachedCount属性。cachedCount的使用方法如下所示。
  1. List() {
  2.   // ...
  3. }.cachedCount(3)
复制代码
此外,HarmonyOS应用框架提供了组件复用能力,可以联合LazyForEach一起使用,进一步优化长列表的性能。组件复用会把组件树大将要移除的组件举行回收,回收的组件会进入到一个回收缓存区。后续创建新组件节点时,会复用缓存区中的节点,节约组件重新创建的时间。
无穷瀑布流

瀑布流的内容出现方式类似瀑布流一样,从上往下依次排列,每一列的高度不肯定相同,团体出现出瀑布流的视觉效果。在瀑布流中,经常使用LazyForEach实现数据按需加载,同时,联合onReachEnd、onApear方法实现无穷瀑布流。
常见失效场景

使用限制

在LazyForEach的使用上,有一些限制条件和限制场景,常见的限制场景总结如下:


  • 键值相同导致渲染庞杂
  • ListItem过于复杂导致丢帧
  • Scroll嵌套List导致按需加载失效
  • GridItem未设置高度导致按需加载失效
检查失效方法

当LazyForEach失效时,大概出现渲染错误、卡顿等征象,开发者可以使用日志、Profiler调优工具等方法来定位具体的问题。常见检查LazyForEach的失效方法有如下几种方式:

  • 通过日志观察键值、子组件创建的情况,参考如下所示。开发者必要注意日志中是否出现相同的键值、子组件出现的次数与实际不符等情况。
  1. LazyForEach(this.data, (lazyForEachItem: string) => {
  2.   ListItem() {
  3.     Row() {
  4.       Text(lazyForEachItem).fontSize(50)
  5.     }.margin({ left: 10, right: 10 })
  6.   }.onAppear(() => {
  7.     // 通过onAppear记录组件创建的次数
  8.     console.info("appear:" + lazyForEachItem)
  9.   })
  10. }, (item: string) => {
  11.   // 在键值生成函数keyGenerator中打印键值
  12.   console.info("key:" + item)
  13.   return item;
  14. })
复制代码

  • 通过Profiler调优工具抓取Trace,可以判定子组件创建的次数。如下图所示,在该帧中出现大量的BuildLazyItem切片,每一次BuildLazyItem对应一次子组件的创建,对比数量可知LazyForEach按需加载失效。


  • 通过HiDumper查看组件信息,判定组件的渲染情况。
键值相同导致渲染庞杂

在LazyForEach的键值生成规则中,每个item对应着一个唯一且持久的键值,用于标识对应的组件。当差别的数据项有相同的键值时,框架大概找不到正确的数据项,导致子组件渲染错误。
ListItem过于复杂导致丢帧

当使用LazyForEach时,如果子组件ListItem过于复杂,在子组件创建时,将产生大量的布局盘算耗时,终极导致该帧丢帧。关键代码如下所示。
  1. @Entry
  2. @Component
  3. struct Index {
  4.   private data: MyDataSource = new MyDataSource();
  5.   aboutToAppear() {
  6.     for (let i = 0; i <= 30; i++) {
  7.       this.data.pushData(`Hello ${i}`)
  8.     }
  9.   }
  10.   build() {
  11.     List({ space: 3 }) {
  12.       LazyForEach(this.data, (lazyForEachItem: string) => {
  13.         ListItem() {
  14.           Column() {
  15.             ForEach(this.data.getAllData(), (forEachItem: string) => {
  16.               ListItem() {
  17.                 Row() {
  18.                   Text(lazyForEachItem + forEachItem).fontSize(50)
  19.                     .onAppear(() => {
  20.                       console.info("appear:" + lazyForEachItem)
  21.                     })
  22.                 }.margin({ left: 10, right: 10 })
  23.               }
  24.             }, (item: string) => item)
  25.           }
  26.         }
  27.       }, (item: string) => item)
  28.     }.cachedCount(5)
  29.   }
  30. }
复制代码
Scroll嵌套List导致按需加载失效

当Scroll容器嵌套List组件加载长列表时,若不指定List的宽高尺寸,则默认加载全部ListItem,导致按需加载失效,甚至会导致应用卡顿、崩溃。
GridItem未设置高度导致按需加载失效

当使用Grid容器时,如果GridItem没有设置高度,会加载全部子组件,设置了GridItem的宽高,会加载Grid显示区域内的子组件。参考案例代码如下:
  1. @Entry
  2. @Component
  3. struct Index {
  4.   private data: MyDataSource = new MyDataSource();
  5.   private scroller: Scroller = new Scroller();
  6.   aboutToAppear() {
  7.     for (let i = 0; i <= 30; i++) {
  8.       this.data.pushData(`Hello ${i}`)
  9.     }
  10.   }
  11.   build() {
  12.     Column() {
  13.       Grid(this.scroller) {
  14.         LazyForEach(this.data, (lazyForEachItem: string) => {
  15.           GridItem() {
  16.             Text(lazyForEachItem)
  17.               .fontSize(50)
  18.               .width('100%')
  19.           }
  20.           .onAppear(() => {
  21.             console.info("appear:" + lazyForEachItem)
  22.           })
  23.         }, (item: string) => {
  24.           return item;
  25.         })
  26.       }
  27.       .columnsTemplate('1fr')
  28.       .enableScrollInteraction(true)
  29.       .width('100%')
  30.       .height(800)
  31.       .cachedCount(5)
  32.     }
  33.     .width('100%')
  34.     .height(700)
  35.   }
  36. }
复制代码
最后

总是有很多小伙伴反馈说:鸿蒙开发不知道学习哪些技术?不知道必要重点掌握哪些鸿蒙开发知识点? 为相识决大家这些学习烦恼。在这准备了一份很实用的鸿蒙全栈开发学习路线与学习文档给大家用来跟着学习。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植……等)技术知识点。

《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://docs.qq.com/doc/DSEd0U29uT3hHbFZK

如何快速入门?

1.根本概念
2.构建第一个ArkTS应用
3.……

鸿蒙开发面试真题(含参考答案):


《OpenHarmony源码解析》:



  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调治子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 装备开发学习手册:https://docs.qq.com/doc/DSHRVT0NZb05PUklN



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莱莱

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表