HarmonyOS NEXT 应用列表场景性能提升实践

在应用的UI开发中,使用列表是一种常规场景,因此,对列表性能进行优化是非常重要的。本文将针对应用开发列表场景的性能提升实践方法展开介绍。

简介

本文会介绍开发列表场景时的4种推荐优化方法,通过独立使用或组合使用这些优化方法,可以获得在启动时间、内存和系统资源方面的平衡,提升性能和用户体验。

懒加载:提供列表数据按需加载能力,解决一次性加载长列表数据耗时长、占用过多资源的问题,可以提升页面响应速度。

缓存列表项:提供屏幕可视区域外列表项长度的自定义调节能力,配合懒加载设置可缓存列表项参数,通过预加载数据提升列表滑动体验。

组件复用:提供可复用组件对象的缓存资源池,通过重复利用已经创建过并缓存的组件对象,降低组件短时间内频繁创建和销毁的开销,提升组件渲染效率。

布局优化:使用扁平化布局方案,减少视图嵌套层级和组件数,避免过度绘制,提升页面渲染效率。

懒加载

原理机制

应用框架为容器类组件的数据加载和渲染提供了2种方式:

方式1,提供ForEach实现一次性加载全量数据并循环渲染。

ForEach(  
  arr: any[], // 需要进行数据迭代的列表数组  
  itemGenerator: (item: any, index?: number) => void, // 子组件生成函数  
  keyGenerator?: (item: any, index?: number) => string // (可选)键值生成函数  
)

方式2,提供LazyForEach实现延迟加载数据并按需渲染。

LazyForEach(  
  dataSource: IDataSource, // 需要进行数据迭代的数据源   
  itemGenerator: (item: any) => void, // 子组件生成函数  
  keyGenerator?: (item: any) => string // (可选) 键值生成函数  
)

ForEach循环渲染的过程如下:

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

在这里插入图片描述

ForEach循环渲染在列表数据量大、组件结构复杂的情况下,会出现性能瓶颈。因为要一次性加载所有的列表数据,创建所有组件节点并完成组件树的构建,在数据量大时会非常耗时,从而导致页面启动时间过长。另外,屏幕可视区外的组件虽然不会显示在屏幕上,但是仍然会占用内存。在系统处于高负载的情况下,更容易出现性能问题,极限情况下甚至会导致应用异常退出。

为了规避上述可能出现的问题,应用框架进一步提供了懒加载方式 。

LazyForEach懒加载的原理如下:

  1. LazyForEach会根据屏幕可视区能够容纳显示的组件数量按需加载数据。
  2. 并根据加载的数据量创建组件,挂载在组件树上,构建出一棵短小的组件树。即,屏幕可以展示多少列表项组件,就按需创建多少个ListItem组件节点挂载在List组件树根节点上。
  3. 屏幕可视区只展示部分组件。当可视区外的组件需要在屏幕内显示时,需要从头完成数据加载、组件创建、挂载组件树这一过程,直至渲染到屏幕上。
    在这里插入图片描述

LazyForEach实现了按需加载,针对列表数据量大、列表组件复杂的场景,减少了页面首次启动时一次性加载数据的时间消耗,减少了内存峰值。可以显著提升页面的能效比和用户体验。

使用场景和限制

如果列表数据较长,一次性加载所有的列表数据创建、渲染页面产生性能瓶颈时,开发者应该考虑使用数据LazyForEach懒加载。

如果列表数据较少,数据一次性全量加载不是性能瓶颈时,可以直接使用ForEach。

限制:ForEach、LazyForEach必须在List、Grid以及Swiper等容器组件内使用,用于循环渲染具有相同布局的子组件。更多懒加载的信息,请参考官方资料LazyForEach:数据懒加载。

LazyForEach懒加载API提供了cachedCount属性,用于配置可缓存列表项数量。除默认加载界面可视部分外,还可以加载屏幕可视区外指定数量(cachedCount)的缓存数据,详见下面“缓存列表项”章节。

实现示例

在List、Grid等容器组件下使用LazyForEach懒加载的示意代码如下:

// LazyForEach要遍历的数据源,为实现接口IDataSource的实例   
private dataList = ...
build() {
   
  Column() {
   
    List() {
   
      LazyForEach(this.dataList, // 数据源          
        (item: ListItemData) => {
    // 根据列表项数据生成对应的组件  
          ListItem() {
   
            this.initItem(item)
          }
        },(item: ListItemData) => item.itemId) // 生成列表项键值
      }
   }
}

接下来将结合示例代码,详细介绍LazyForEach懒加载的实现过程,包含下图所示的三部分内容:

1、准备数据源类

2、遍历数据源创建列表组件项

3、为列表项指定唯一的键值编码

在这里插入图片描述

代码实现如下。首先,在使用LazyForEach数据懒加载之前,需要实现懒加载数据源接口类IDataSource。数据源接口类提供了获取数据总量,返回指定索引位置的数据,以及注册、注销数据监听器的接口。编写一个实现数据源接口IDataSource的数据源类BasicDataSource,该类包含数据变更监听器DataChangeListener类型的实例变量listeners,用于维护注册的数据变更监听器,在数据变更时调用相应的回调函数。每一个listener实例对应一个ArkUI框架侧的LazyForEach实例,数据源数据发生变更时,listener实例会通知LazyForEach需要触发界面刷新。

BasicDataSource是一个抽象类,不同的具体列表页面的数据源需要根据业务场景分别实现该抽象类。以聊天列表场景为例,数据源具体类ChatListData实现如下。其中,列表项数组变量chatList: Array用于为List子组件提供数据。ChatModel类表示聊天列表中列表项,包含联系人信息、最后一条消息内容、时间戳、未读消息数量等信息;totalCount()和getData(index: number)是实现数据源接口类IDataSource中定义的方法,用于给LazyForEach提供数据,应用框架会调用这些方法;addData()和pushData()方法为数据源类中定义的方法,可用于给数据源增加数据。需要注意的是,在这2个方法中需要调用notifyDataAdd方法,用于调用DataChangeListener中的接口来触发LazyForEach刷新。

class ChatListData extends BasicDataSource {
     
    /**  
    * 聊天列表项数组  
    */  
    private chatList: Array<ChatModel> = []  
    /**  
    * 数据源的数据总量  
    */  
    public totalCount(): number {
     
        return this.chatList.length  
    }  

    /**  
    * 返回指定索引位置的数据  
    */  
    public getData(index: number): ChatModel {
     
        return this.chatList[index]  
    }  
    /**  
    * 指定位置添加一条聊天列表数据  
    */  
    public addData(index: number, data: ChatModel): void {
     
        this.chatList.splice(index, 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值