HarmonyOS应用框架为容器类组件的数据加载和渲染提供了2种方式:
- 方式一,循环渲染
通过循环渲染(ForEach)从数组中获取数据,并为每个数据项创建相应的组件,可减少代码复杂度。
- 方式二,数据懒加载
通过数据懒加载(LazyForEach)从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。
ForEach循环渲染的过程如下:
- 从列表数据源一次性加载全量数据。
- 为列表数据的每一个元素都创建对应的组件,并全部挂载在组件树上。即,ForEach遍历多少个列表元素,就创建多少个ListItem组件节点并依次挂载在List组件树根节点上。
- 列表内容显示时,只渲染屏幕可视区内的ListItem组件,可视区外的ListItem组件滑动进入屏幕内时,因为已经完成了数据加载和组件创建挂载,直接渲染即可。
LazyForEach懒加载的原理和渲染过程如下:
- LazyForEach会根据屏幕可视区能够容纳显示的组件数量按需加载数据。
- 加载的数据量创建组件,挂载在组件树上,构建出一棵短小的组件树。即,屏幕可以展示多少列表项组件,就按需创建多少个ListItem组件节点挂载在List组件树根节点上。
- 可视区只展示部分组件。当可视区外的组件需要在屏幕内显示时,需要从头完成数据加载、组件创建、挂载组件树这一过程,直至渲染到屏幕上。
使用规则
ForEach、LazyForEach必须在容器组件内使用,且仅有List、Grid、Swiper以及WaterFlow组件支持LazyForEach的数据懒加载功能,用于循环渲染具有相同布局的子组件,其他组件仍然是一次性加载所有的数据。
从测试数据可以看出:
- 在100条数据范围内ForEach和LazyForEach差距不大,总体而言两者各项性能指标都在可接受范围内,而ForEach代码逻辑比LazyForEach简单,此场景下使用ForEach即可。
- 据大于1000条,特别是当数据达到10000条时,ForEach在列表渲染、应用内存占用、丢帧率等各个方面都会有“指数级别”的显著劣化,滑动会出现明显的卡顿,甚至会出现应用crash等现象。
- LazyForEach除了最后内存稍微增大以外,其列表渲染时间、丢帧率都不会出现明显变化,具有较好的性能。
缓存列表项
如果不提前缓存部分数据,当下滑到列表最底端时,再快速下滑,可能会引起“滑动白块”的现象。
LazyForEach懒加载可以通过设置cachedCount来指定缓存数量,在设置cachedCount后,除屏幕内显示的ListItem组件外,还会预先将屏幕可视区外指定数量的列表项数据缓存。
LazyForEach添加了cachedCount缓存列表项后,其渲染过程如下:
- 首先,请求n+cachedCount条数据,并在屏幕上显示n条数据。
- 当列表滑动,缓存列表项需要从屏幕可视区外进入可视区内时,此时只用渲染组件即可,相比不设置cachedCount提升了显示效率。
- 当列表不断滑动,屏幕可视区外缓存的列表项数量少于cachedCount设置数量时,会触发列表项数据加载事件,继续预加载下一组缓存列表项(cachedCount个)。
- 当上滑下滑间隔进行时,列表两个方向分别缓存cachedCount条数据。
- 如果不显式设置cachedCount,cachedCount默认为1。
使用场景
缓存列表项适合加载列表项数据请求比较耗时的场景。比如,滑动列表中含有短视频、高清图片等数据量比较大的资源,可以通过预先从网络加载并缓存相关数据,缩短渲染前的准备时间,提升列表响应速度。
`@Component
export struct DiscoverView {
private data: ArticleListData = new ArticleListData();
......
build() {
List() {
LazyForEach(this.data, (item: LearningResource) => {
ListItem() {
ArticleCardView({
articleItem: item
......
})
}
}, (item: LearningResource) => item.id)
}.cachedCount(3);
}
}` </pre>
性能分析
一般而言,缓存的cachedCount=n/2(n为一屏显示的列表数)的时候,效果较好。在实际开发中也要根据实际场景合理去设置缓存数量,例如列表项中需要显示网络数据,而网络数据加载较慢,为了提升列表信息的浏览效率和浏览体验,我们可以适当的多设置一些缓存数量(cachedCount大于n/2);如果列表中需要加载一些大图或者视频等,这些数据占用的内存较大,为了减少内存占用,我们需要适当减少缓存数量的设置(cachedCount小于n/2)。因此,在实际场景中,需要不断尝试验证,设置适当的缓存数量,来达到体验和内存的平衡。
组件复用
原理介绍
HarmonyOS应用框架提供了组件复用能力,可复用组件从组件树上移除时,会进入到一个回收缓存区。后续创建新组件节点时,会复用缓存区中的节点,节约组件重新创建的时间。尤其在列表等场景下,其自定义子组件具有相同的组件布局结构,列表更新时仅有状态变量等数据差异。通过组件复用可以提高列表页面的加载速度和响应速度。
组件复用机制如下:
- 标记为@Reusable的组件从组件树上被移除时,组件和其对应的JSView对象都会被放入复用缓存中。
- 当列表滑动新的ListItem将要被显示,List组件树上需要新建节点时,将会从复用缓存中查找可复用的组件节点。
- 找到可复用节点并对其进行更新后添加到组件树中。从而节省了组件节点和JSView对象的创建时间。
组件复用生效的条件是:
- 自定义组件被@Reusable装饰器修饰,即表示其具备组件复用的能力;
- 在一个自定义父组件下创建出来的具备组件复用能力的自定义子组件,在可复用自定义组件从组件树上移除之后,会被加入到其自定义父组件的可复用节点缓存中;
- 在一个自定义父组件下创建可复用的子组件时,若其父自定义组件的可复用节点缓存中有对应类型的可复用子组件,会通过更新可复用子组件的方式,快速创建可复用子组件;
- ForEach循环渲染会一次性加载全量数据,因此不支持组件复用。
名词介绍:
- @Reusable表示组件可以被复用,结合LazyForEach懒加载一起使用,可以进一步解决列表滑动场景的瓶颈问题,提供滑动场景下高性能创建组件的方式来提升滑动帧率。
- CustomNode是一种自定义的虚拟节点,它可以用来缓存列表中的某些内容,以提高性能和减少不必要的渲染。通过使用CustomNode,可以实现只渲染当前可见区域内的数据项,将未显示的数据项缓存起来,从而减少渲染的数量,提高性能。
- RecycleManager是一种用于优化资源利用的回收管理器。当一个数据项滚出屏幕时,不会立即销毁对应的视图对象,而是将该视图对象放入复用池中。当新的数据项需要在屏幕上展示时,RecycleManager会从复用池中取出一个已经存在的视图对象,并将新的数据绑定到该视图上,从而避免频繁的创建和销毁过程。通过使用RecycleManager,可以大大减少创建和销毁视图的次数,提高列表的滚动流畅度和性能表现。
- CachedRecycleNodes是CustomNode的一个集合,常是用于存储被回收的CustomNode对象,以便在需要时进行复用。
使用场景和规则
使用场景
若业务实现中存在以下场景,并成为UI线程的帧率瓶颈,推荐使用组件复用:
- 列表滚动(本例中的场景):当应用需要展示大量数据的列表,并且用户进行滚动操作时,频繁创建和销毁列表项的视图可能导致卡顿和性能问题。在这种情况下,使用列表组件的组件复用机制可以重用已经创建的列表项视图,提高滚动的流畅度。
- 动态布局更新:如果应用中的界面需要频繁地进行布局更新,例如根据用户的操作或数据变化动态改变视图结构和样式,重复创建和销毁视图可能导致频繁的布局计算,影响帧率。在这种情况下,使用组件复用可以避免不必要的视图创建和布局计算,提高性能。
- 地图渲染:在地图渲染这种场景下,频繁创建和销毁数据项的视图可能导致性能问题。使用组件复用可以重用已创建的视图,只更新数据的内容,减少视图的创建和销毁,能有效提高性能。
以上场景中,为了避免UI线程的帧率瓶颈问题,推荐使用组件复用来提高应用的性能和用户体验。组件复用可以避免不必要的视图创建和销毁,减少布局计算和绘制操作,从而提高界面的流畅度和响应速度。
使用规则
组件复用的使用规则如下:
- 使用 @Reusable标识:@Reusable标识自定义组件具备可复用的能力,它可以被添加到任意的自定义组件上,但是开发者需要小心处理自定义组件的创建流程和更新流程以确保自定义组件在复用之后能展示出正确的行为;
- 缓存和复用范围:可复用自定义组件的缓存和复用只能发生在同一父组件下,无法在不同的父组件下复用同一自定义节点的实例。e.g. A组件是可复用组件,其也是B组件的子组件,并进入了B组件的可复用节点缓存中,但是在C组件中创建A组件时,无法使用B组件缓存的A组件;
- 组件结构无显著变化:自定义组件的复用带来的性能提升主要体现在节省了自定义组件的JS对象的创建时间并复用了自定义组件的组件树结构,若应用开发者在自定义组件复用的前后使用渲染控制语法显著的改变了自定义组件的组件树结构,那么将无法享受到组件复用带来的性能提升;
- 仅在特定场景触发:组件复用仅发生在存在可复用组件从组件树上移除并再次加入到组件树的场景中,若不存在上述场景,将无法触发组件复用。e.g. 使用ForEach渲染控制语法创建可复用的自定义组件,由于ForEach渲染控制语法的全展开属性,不能触发组件复用。
超过了11.1ms而丢帧。
性能分析
组件未复用时
将ForEach改造为了LazyForEach,并且添加了缓存项(cachedCount=3),当匀速滑动这个列表时,每隔若干帧时会稳定的丢帧,且会规律、重复的出现这个问题
红色区域出现了丢帧,这是因为缓存区中的最上面的一个ListItem渲染到页面上时,会执行BuildLazyItem操作,此部分会耗时10.277ms,导致本帧总体耗时达到了13.430ms,超过了11.1ms而丢帧。
组件复用后
将代码进行改造,对复用组件ArticleCardView添加@Reusable注解,启用组件复用的相关代码后,以相同均匀速度滑动这个列表,得到的应用帧率检测情况如下:
此部分的BuildLazyItem只耗时0.749ms,远远低于之前未进行复用的10.277ms,对复用前后的耗时进行分析,得到的数据如下表所示:
可以发现之前的丢帧情况得到明显缓解,从图4可以看出,列表滑动时(15.8s的区间段内)都是绿色,丢帧率为0%,不会出现图1中“有规律、且重复”的红色丢帧情况。这是因为,List列表开启了组件复用,不会执行BuildLazyItem这个耗时操作(耗时10.277ms),后续创建新组件节点时,会直接复用缓存
布局优化
原理介绍
列表不同于其他布局,包含了大量重复循环的ListItem,所以对每一个ListItem的布局优化格外重要。错误的布局方式可能会导致组件树和嵌套层数过多,在创建和布局绘制阶段产生较大的性能开销,导致界面卡顿。合理使用布局,减少嵌套层数,能提高布局效率。
针对“HMOS世界” 中首屏的长列表,可以对ListItem进行布局优化,把线性布局修改为相对布局,就可以将最大嵌套层级从5层降低到2层。当进行列表循环渲染的时候,特别是当数据量大时,就能对页面性能产生一定的优化。当然这个例子还过于简单,优化空间是有限的;但是当列表元素较为复杂时,通过减少布局嵌套层级,减少过度绘制可以产生较大的性能收益。区中的节点(耗时0.97ms),这样就大幅节约了组件重新创建的时间。
场景案例
为了对比布局嵌套层级对List列表滑动性能的影响,对相关代码进行了改造,原始代码是线性布局,最大嵌套层级为5层;通过相对布局对代码进行优化,使得其最大嵌套层级为2层;同时,刻意将布局进行过度嵌套,使得其最大嵌套层级为25层。
可以看出,因为布局过度嵌套会导致应用内存增加,且会影响应用的帧率导致丢帧增加,所以开发者在写列表这类循环组件的代码时,需要特别考虑对其布局进行优化。一般而言布局的最大嵌套层级控制在5-8层左右即可,过度的优化布局会导致代码开发难度加大,代码不易于阅读理解,增加后续的维护成本,不利于多设备的适配,且也不会带来特别显著的性能提升。
写在最后
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。
这份鸿蒙(HarmonyOS NEXT)文档包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
希望这一份鸿蒙学习文档能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习文档
鸿蒙(HarmonyOS NEXT)5.0最新学习路线
有了路线图,怎么能没有学习文档呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习文档
《鸿蒙 (OpenHarmony)开发入门教学视频》
《鸿蒙生态应用开发V3.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
●ArkTS语言
●安装DevEco Studio
●运用你的第一个ArkTS应用
●ArkUI声明式UI开发
.……
《鸿蒙开发进阶》
●Stage模型入门
●网络管理
●数据管理
●电话服务
●分布式应用开发
●通知与窗口管理
●多媒体技术
●安全技能
●任务管理
●WebGL
●国际化开发
●应用测试
●DFX面向未来设计
●鸿蒙系统移植和裁剪定制
……
《鸿蒙进阶实战》
●ArkTS实践
●UIAbility应用
●网络案例
……