鸿蒙开发之LazyForEach及刷新问题

一、定义以及用途

在开发过程中会遇到list、grid的item过多的情况。例如某些新闻类、某些购物类的页面,如果我们有大量的数据,直接通过ForEach进行全量渲染会直接把手机卡死。这样肯定是不行的,这个时候LazyForEach就该登场了。

LazyForEach主要是用来针对列表数据量大、列表组件复杂的场景,减少了页面一次性加载数据的时间消耗,减少了内存峰值。可以显著提升页面的能效比和用户体验。实现原理就是它会从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach,框架会根据滚动容器可视区域按需创建组件,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。(根据原生开发思想,就是cell的复用机制,绘制可见区域,超过屏幕后回收,新出现的优先使用复用池的cell)。通过lazyForEach来进行性能的优化或者在APP启动的页面采用这个方案也提升了APP的启动(这也算是项目的一个小优化了😄)。

二、使用限制

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

如果列表数据较少,数据一次性全量加载没有性能问题时,可以直接使用ForEach。

LazyForEach懒加载API提供了cachedCount属性,可以通过设置缓存列表项数量。除默认加载界面可视部分外,还可以加载屏幕可视区外指定数量(cachedCount)的缓存数据。系统默认的cachedCount为1。我们在开发的时候可以默认缓冲一屏幕的数据,来优化用户体验。

以下图片截选自官网

三、API使用

首先,需要我们定义类来实现IDataSource接口方法,直接拷贝官网的就可以了,如下

export class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: string[] = [];

  public totalCount(): number {
    return 0;
  }

  public getData(index: number): string {
    return this.originDataArray[index];
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }

  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }
}

然后,我们需要定义我们真实处理数据的类,主要用来做我们想做的事情,例如添加元素、删除元素、全部更新等,例如我的例子

import {BasicDataSource} from './BasicDataSource'

export class CommercialMoneyMonthDataSource extends BasicDataSource {
  
  //列表数据源
  list: any[] = []

  //数据数量
  totalCount(): number {
    return this.list.length
  }

  //获取某条数据
  getData(index: number): any {
    return this.list[index]
  }

  //刷新全部数据
  freshData(data:any[]) {
    this.list = data
    this.notifyDataReload()
  }

  //指定位置添加一条数据
   addData(index: number, data: any): void {
    this.list.splice(index, 0, data)
    this.notifyDataAdd(index)
  }

  //结尾添加一条数据
   pushData(data: any): void {
    this.list.push(data)
    this.notifyDataAdd(this.list.length - 1)
  }
}

最后,组件中使用,我的开发场景需要数据全量刷新,所以测试的是全部刷新数据

@Entry
@Component
struct TestLazyForEachPage {
  @State message: string = '点击全部改变'

  aboutToAppear() {
    for (let i = 100; i >= 80; i--) {
      this.data.pushData(`Hello ${i}`)
    }
  }

  private data: MyDataSource = new MyDataSource();

  build() {

    Column() {
      Text(this.message)
        .width('100%')
        .height(80)
        .fontSize(25)
        .onClick(() => {
          let newData: string[] = []
          for (let i = 10; i <= 30; i++) {
            newData.push(`Hello ${i}`)
          }
          this.data.freshData(newData)
          Prompt.showToast({message:'改变了'})
        })

      List({ space: 3 }) {
        LazyForEach(this.data, (item: string) => {
          ListItem() {
            Row() {
              Text(item).fontSize(50)
                .onAppear(() => {
                  console.info("appear:" + item)
                })
            }.margin({ left: 10, right: 10 })
          }
          .onClick(() => {
            this.data.pushData(`Hello ${this.data.totalCount()}`);
          })
        }, (item: string) => item)
      }.cachedCount(5)
    }
    .width('100%')
    .height('100%')
  }
}

个人感觉,LazyForEach的理解还算好理解,就是写起来有点麻烦了。其实官网提供的BasicDataSource类完全可以封装好提供API出来,这又没有什么好改动的。

四、开发中遇到的问题

开发中,我想达到数据改变,全量刷新的效果。

最开始,因为官网没有给全量刷新的例子。又有@state的概念影响,感觉给组件中的data加上@state装饰器,数据改变就可以刷新了。所以,在数据改变那里直接创建了一个新的dataSource给this.data,也就是数据改变那里我这样写的

    //使用了@State装饰器
  @State private data: MyDataSource = new MyDataSource();

  Text(this.message)
        .width('100%')
        .height(80)
        .fontSize(25)
        .onClick(() => {
            //创建了一个新的MyDataSource
           let newData = new MyDataSource();
          for (let i = 10; i <= 30; i++) {
            newData.pushData(`Hello ${i}`)
          }
           //改变this.data
          this.data = newData
          Prompt.showToast({message:'改变了'})
        })

本以为,LazyForEach的数据源我传递this.data,然后数据源已经改成了我新创建的MyDataSource了就会刷新,然而,并没有效果。官网也确实说了,想要刷新需要通过listener去刷新。所以,后来在CommercialMoneyMonthDataSource中定义了freshData的方法来刷新数据,并通知观察者。把重新创建一个MyDataSource去掉,而是通过freshData方法把最新的数据源传递进去。

然后,因为我实际项目中我的datasource中封装的是字符串(第1期、第2期、第3期。。。),而数据改变但字符串并没有改动,而是列表中其他的值的改变。所以,key直接使用(item: string) => item这样做可见区域并不会刷新,想到需要使用变动的key来刷新,我采用的是key的拼接方法(item: string) => item+xxx。xxx是我项目中数据每次变动的时候不同的值。

综上,LazyForEach数据源不刷新我踩的坑是

  1. dataSource不需要使用@state装饰器,用了也没用,并不会随数据刷新UI
  2. dataSource要使用同一个,不要生成一个新dataSource的去赋值
  3. key一定要唯一,如果想要UI改变,那么key需要更新(这个很关键

  • 25
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
华为鸿蒙HarmonyOS开发整理资料汇总,共38份。 1学前必读:HarmonyOS学习资源主题分享 2学前必读:OpenHarmony-联盟生态资料合集 3-1.HarmonyOS概述:技术特性 3-2.HarmonyOS概述:开发工具与平台 3-3.HarmonyOS概述:系统安全 3-4.HarmonyOS概述:系统定义 3-5.HarmonyOS概述:下载与安装软件 3-6.HarmonyOS概述:应用开发基础知识 3-7.HarmonyOS概述:最全HarmonyOS文档和社区资源使用技巧 4-1.生态案例:【开发者说】重塑经典,如何在HarmonyOS手机上还原贪吃蛇游戏 4-2.生态案例:HarmonyOLabo涂鸦鸿蒙亲子版 4-3.生态案例:HarmonyOS分镜头APP案例 4-4.生态案例:HarmonyOS时光序历史学习案例 4-5.生态案例:HarmonyOS先行者说 宝宝巴士携手HarmonyOS共同打造儿童教育交互新体验 4-6.生态案例:HarmonyOS智能农场物联网连接实践 4-7.生态案例:分布式开发样例,带你玩转多设备 4-8.生态案例:华为分布式日历应用开发实践 5-1.【Codelab】HarmonyOS基于图像模块实现图库图片的四种常见操作 5-2.【CodeLab】手把手教你创建第一个手机“Hello World” 5-3.【Codelab】如此简单!一文带你学会15个HarmonyOS JS组件 5-4.【Codelab】懒人“看”书新法—鸿蒙语音播报,到底如何实现? 5-5.【Codelab】基于AI通用文字识别的图像搜索,这波操作亮了 5-6.【Codelab】开发样例概览 6-1.技术解读之HarmonyOS轻量JS开发框架与W3C标准差异分析 6-2.技术解读之HarmonyOS驱动加载过程分析 6-3.技术解读之HarmonyOS组件库使用实践 6-4.技术解读之华为架构师解读:HarmonyOS低时延高可靠消息传输原理 6-5.技术解读之解密HarmonyOS UI框架 6-6.技术解读之如何从OS框架层面实现应用服务功能解耦 7-1.常见问题之HarmonyOS元服务的设计与开发解析 7-2.常见问题之Java开发 7-3.常见问题之JS开发 7-4.常见问题之模拟器登录 7-5.常见问题之模拟器运行 7-6.常见问题之如何使用JsJava开发HarmonyOS UI 7-7.常见问题之应用配置 7-8.常见问题之预览器运行 8【视频合集】入门到进阶视频学习资料合集30+

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值