【鸿蒙开发】[性能优化]ForEach与LazyForEach有什么区别?

以下是 ForEach 的语法及其与 LazyForEach 的区别。

ForEach 的语法

ForEach 的基本使用方式如下:

ForEach(
  arr: Array<any>,                                   // 数据源数组
  itemGenerator: (item: any, index?: number) => void, // 子组件生成函数
  keyGenerator?: (item: any, index?: number) => string // 键值生成函数(可选)
)

示例代码

以下是 ForEach 的一个完整示例:

@Entry
@Component
struct Parent {
  @State simpleList: Array<string> = ['one', 'two', 'three'];

  build() {
    Row() {
      Column() {
        ForEach(this.simpleList, (item: string, index: number) => {
          ChildItem({ item: item })
        }, (item: string, index: number) => item)
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ChildItem {
  @Prop item: string;

  build() {
    Text(this.item)
      .fontSize(50)
  }
}

LazyForEach 的语法

LazyForEach 的基本使用方式如下:


LazyForEach(
  dataSource: IDataSource,                             // 数据源
  itemGenerator: (item: any, index: number) => void,   // 子组件生成函数
  keyGenerator?: (item: any, index: number) => string // 键值生成函数(可选)
)

示例代码

以下是 LazyForEach 的一个完整示例:

@Entry
@Component
struct TestLazyForEachPage {
  @State message: string = '点击全部改变';
  private data: MyDataSource = new MyDataSource();

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

  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, index: number) => {
          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, index: number) => item + index);
      }.cachedCount(5);
    }
    .width('100%')
    .height('100%');
  }
}

ForEach 与 LazyForEach 的区别

1.数据源类型:

○ForEach:直接接受一个数组作为数据源。
○LazyForEach:接受一个实现了 IDataSource 接口的对象作为数据源。

2.渲染策略:

○ForEach:一次性渲染所有数据项,适用于数据量较少的情况。
○LazyForEach:按需渲染数据项,只渲染可视区域内的数据项,适用于数据量较大的情况,提升性能。

3.内存使用:

○ForEach:会一次性加载所有的数据项,内存使用较高。
○LazyForEach:根据可视区域按需加载数据项,并回收滑出可视区域的数据项,内存使用较低。

4.组件复用:

○ForEach:没有内置的组件复用机制。
○LazyForEach:类似于原生开发中的 cell 复用机制,滑出可视区域的组件会被回收,新出现的组件优先使用复用池中的组件。

5.性能优化:

○ForEach:适用于数据量较少且性能要求不高的场景。
○LazyForEach:适用于数据量较大且性能要求较高的场景,显著提升页面性能和用户体验。

6.数据监听:

○ForEach:没有专门的数据监听机制,依赖于数组变化触发的 UI 更新。
○LazyForEach:需要通过 DataChangeListener 来监听数据变化,手动通知数据变动以刷新 UI。

使用场景

●ForEach:适用于静态数据或数据量较小的场景,例如简单的列表展示。
●LazyForEach:适用于动态数据或数据量较大的场景,例如新闻列表、商品列表等需要懒加载的页面。

通过理解上述区别,开发者可以根据具体需求选择合适的渲染方式来优化性能和用户体验。

另外, 关于LazyForEach实现IDataSource 接口

为什么需要实现 IDataSource 接口?

LazyForEach 组件用于处理大数据量时的按需渲染和懒加载,IDataSource 接口提供了一种标准化的方法来管理和访问这些数据。通过实现 IDataSource 接口,可以确保 LazyForEach 组件能够高效地处理数据,并在数据发生变化时正确地刷新 UI。

IDataSource 接口的作用

IDataSource 接口定义了一组方法,用于获取数据、监听数据变化等操作。具体包括:

1.totalCount() : 返回数据源中的数据总数。
2.getData(index: number) : 根据索引获取对应的数据项。
3.registerDataChangeListener(listener: DataChangeListener) : 注册数据变化的监听器。
4.unregisterDataChangeListener(listener: DataChangeListener) : 注销数据变化的监听器。
5.notifyDataReload() : 通知组件重新加载所有数据。
6.notifyDataAdd(index: number) : 通知组件在指定索引处添加数据。
7.notifyDataChange(index: number) : 通知组件在指定索引处的数据发生变化。
8.notifyDataDelete(index: number) : 通知组件在指定索引处删除数据。

实现 IDataSource 接口

通过实现 IDataSource 接口,开发者可以自定义数据源的行为,并确保 LazyForEach 能够正确地处理数据加载、数据变化等操作。下面是一个完整的实现示例:

interface IDataSource {
  totalCount(): number;
  getData(index: number): any;
  registerDataChangeListener(listener: DataChangeListener): void;
  unregisterDataChangeListener(listener: DataChangeListener): void;
  notifyDataReload(): void;
  notifyDataAdd(index: number): void;
  notifyDataChange(index: number): void;
  notifyDataDelete(index: number): void;
}

interface DataChangeListener {
  onDataReloaded(): void;
  onDataAdd(index: number): void;
  onDataChange(index: number): void;
  onDataDelete(index: number): void;
}

class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private data: any[] = [];

  totalCount(): number {
    return this.data.length;
  }

  getData(index: number): any {
    return this.data[index];
  }

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

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const index = this.listeners.indexOf(listener);
    if (index >= 0) {
      this.listeners.splice(index, 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));
  }

  // Methods to manipulate data
  addData(data: any): void {
    this.data.push(data);
    this.notifyDataAdd(this.data.length - 1);
  }

  updateData(index: number, data: any): void {
    this.data[index] = data;
    this.notifyDataChange(index);
  }

  deleteData(index: number): void {
    this.data.splice(index, 1);
    this.notifyDataDelete(index);
  }
}

使用 LazyForEach 与 IDataSource

在使用 LazyForEach 时,需要将数据源传递给 LazyForEach 组件,并通过实现的 IDataSource 接口来管理数据。以下是使用 LazyForEach 的示例:

@Entry
@Component
struct TestLazyForEachPage {
  private data: BasicDataSource = new BasicDataSource();

  aboutToAppear() {
    for (let i = 0; i < 20; i++) {
      this.data.addData(`Item ${i}`);
    }
  }

  build() {
    Column() {
      List({ space: 3 }) {
        LazyForEach(this.data, (item: string, index: number) => {
          ListItem() {
            Row() {
              Text(item)
                .fontSize(20)
                .onAppear(() => {
                  console.info(`appear: ${item}`);
                });
            }.margin({ left: 10, right: 10 });
          }
          .onClick(() => {
            this.data.updateData(index, `${item} (clicked)`);
          });
        }, (item: string, index: number) => `${item}_${index}`);
      }.cachedCount(5);
    }
    .width('100%')
    .height('100%');
  }
}

结论

通过实现 IDataSource 接口,可以为 LazyForEach 提供灵活、高效的数据管理方式,确保在处理大数据量时能够按需加载数据,提升性能和用户体验。这也是 LazyForEach 与 ForEach 的主要区别之一:ForEach 适用于小数据量的全量渲染,而 LazyForEach 则专注于大数据量的按需渲染和懒加载。

写在最后

有很多小伙伴不知道该从哪里开始学习鸿蒙开发技术?也不知道鸿蒙开发的知识点重点掌握的又有哪些?自学时频繁踩坑,导致浪费大量时间。结果还是一知半解。所以有一份实用的鸿蒙(HarmonyOS NEXT)全栈开发资料用来跟着学习是非常有必要的。

获取完整版高清学习资料,请点击→鸿蒙全栈开发学习资料(安全链接,请放心点击)

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了

最新鸿蒙全栈开发学习线路在这里插入图片描述

鸿蒙HarmonyOS开发教学视频

在这里插入图片描述

大厂面试真题

在这里插入图片描述

在这里插入图片描述

鸿蒙OpenHarmony源码剖析

在这里插入图片描述

这份资料能帮住各位小伙伴理清自己的学习思路,更加快捷有效的掌握鸿蒙开发的各种知识。有需要的小伙伴自行领取,,先到先得~无套路领取!!

获取这份完整版高清学习资料,请点击→鸿蒙全栈开发学习资料(安全链接,请放心点击)

  • 12
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值