LazyForEach ArkTS中的性能加速器

2024-12-05 05:42:10
260次阅读
0个评论
最后修改时间:2024-12-05 14:32:04

伙计们伙计们,在OpenHarmony开发的世界里,性能和流畅度是用户体验的关键。而LazyForEach则是我们在面对长列表或大数据集时的一把好用的工具。这家伙不是简单的循环语句,而是一种智能的数据迭代方式,它能够按需加载数据,只在必要时创建组件,从而大幅度提升应用的性能和响应速度。

LazyForEach的工作原理

LazyForEach这家伙的聪明之处在于,它只在数据进入可视区域时才创建对应的UI组件,一旦数据滑出屏幕,对应的组件就会被销毁,这样就能大大减少内存的占用。这就像是在告诉应用:“嘿,兄弟,别一次性把所有家伙都拿出来,只把需要的拿出来就行了。”

使用LazyForEach的限制和注意事项

虽然LazyForEach很酷,但也不是没有规矩的。以下是一些使用时的注意事项:

  1. 容器组件限制LazyForEach必须在支持懒加载的容器组件内使用,比如ListGridSwiperWaterFlow。其他的组件,比如普通的ColumnRow,就不支持这种懒加载方式。

  1. 单次使用:在同一个容器组件内,只能使用一个LazyForEach。别想着同时用多个LazyForEach或者和其他循环语句混用,这样会导致不可预知的问题。

  1. 子组件限制:每次迭代中,LazyForEach只能创建一个子组件。这意味着你不能在一个迭代中创建多个根组件。

  1. 键值生成器:为了确保每个组件都能被正确地识别和复用,LazyForEach需要一个键值生成器来为每个数据项生成唯一的键值。如果两个数据项的键值相同,就会导致渲染问题。

  1. 数据更新LazyForEach必须使用DataChangeListener对象进行更新。直接重新赋值给数据源是不会触发UI刷新的。

代码实现和重构

下面我们通过一些代码示例来深入了解LazyForEach的用法。

数据源接口封装

// BasicDataSource实现了IDataSource接口,用于管理listener监听,以及通知LazyForEach数据更新
class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: string[] = [];

  public totalCount(): number {
    return 0;
  }

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

  // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  // 通知LazyForEach组件需要重载所有子组件
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知LazyForEach组件需要在index对应索引处添加子组件
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
      // 写法2:listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]);
    })
  }

  // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
      // 写法2:listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]);
    })
  }

  // 通知LazyForEach组件需要在index对应索引处删除该子组件
  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
      // 写法2:listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]);
    })
  }

  // 通知LazyForEach组件将from索引和to索引处的子组件进行交换
  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
      // 写法2:listener.onDatasetChange(
      //         [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]);
    })
  }

  notifyDatasetChange(operations: DataOperation[]): void {
    this.listeners.forEach(listener => {
      listener.onDatasetChange(operations);
    })
  }
}

示例1:基础使用

// 实现数据源
class MyDataSource implements BasicDataSource {
  private listeners: DataChangeListener[] = [];
  private dataArray: string[] = [];

  // 实现接口方法
  totalCount(): number {
    return this.dataArray.length;
  }

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

  // 省略其他方法的实现...
}

// 组件实现
@Entry@Componentstruct MyListComponent {
  private dataSource: MyDataSource = new MyDataSource();

  build() {
    List({ space: 3 }) {
      LazyForEach(this.dataSource, (item: string) => {
        ListItem() {
          Text(item).fontSize(50);
        }
      }, (item: string) => item); // 使用item本身作为键值
    }
    .cachedCount(5); // 缓存5个项目
  }
}

示例2:动态数据操作

// 扩展MyDataSource类,添加数据操作方法
class MyDataSource extends BasicDataSource {
  // 省略其他方法...

  public addData(data: string): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

// 组件实现
@Entry
@Componentstruct 
MyListComponent {
  private dataSource: MyDataSource = new MyDataSource();

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

  build() {
    List({ space: 3 }) {
      LazyForEach(this.dataSource, (item: string) => {
        ListItem() {
          Row() {
            Text(item).fontSize(50)
              .onClick(() => {
                // 动态添加数据
                this.dataSource.addData(`New Item ${this.dataSource.totalCount()}`);
              });
          }
        };
      }, (item: string) => item);
    }
    .cachedCount(5);
  }
}

示例3:解决删除数据时的索引问题

// 组件实现
@Entry
@Componentstruct 
MyListComponent {
  private dataSource: MyDataSource = new MyDataSource();

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

  build() {
    List({ space: 3 }) {
      LazyForEach(this.dataSource, (item: string, index: number) => {
        ListItem() {
          Row() {
            Text(item).fontSize(50)
              .onClick(() => {
                // 删除当前项,并重新加载数据
                this.dataSource.dataArray.splice(index, 1);
                this.dataSource.notifyDataDelete(index);
                this.dataSource.notifyDataReload();
              });
          }
        };
      }, (item: string, index: number) => `${item}_${index}`); // 使用item和index组合作为键值
    }
    .cachedCount(5);
  }
}

LazyForEach是OpenHarmony开发中的一个能力,它通过智能的数据加载和组件创建,帮助我们构建高性能的应用。掌握LazyForEach的使用,能够让你在处理大量数据时游刃有余。记住,合理使用这家伙,你的应用就能跑得更快,用户的笑容也会更灿烂。

收藏00

登录 后评论。没有帐号? 注册 一个。