HarmonyOS NEXTにおけるLazyForEachを用いたデータ遅延ロードの実装

LazyForEachによるデータ遅延ロード

LazyForEachはデータソースから必要に応じてアイテムを反復処理し、対応するコンポーネントを動的に生成します。スクロール可能なコンテナ内で使用すると、可視領域内のアイテムのみをレンダリングし、領域外のコンポーネントは破棄されてメモリ使用量が削減されます。

実装制約事項

  • List, Grid, Swiper, WaterFlowコンテナ内でのみ使用可能(cachedCountでバッファ領域設定)
  • 反復処理ごとに単一の子コンポーネント生成が必須
  • キー生成関数は各データに一意の値を割り当てること
  • 状態変数の変更ではUI更新されないためDataChangeListenerを使用
  • @Reusableデコレータと併用でコンポーネント再利用が有効化

キー生成メカニズム

各アイテムに永続的な一意キーを自動生成し、キー変更時にコンポーネントを再作成します。カスタムキー生成関数が未定義の場合、デフォルトで(item, index) => viewId + '-' + indexが適用されます。

ローカルデータ実装例

class DataSourceBase implements IDataSource {
  private observers: DataChangeListener[] = [];
  private items: string[] = [];

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

  getItem(index: number): string {
    return this.items[index];
  }

  registerListener(observer: DataChangeListener): void {
    if (!this.observers.includes(observer)) {
      this.observers.push(observer);
    }
  }

  addItem(data: string): void {
    this.items.push(data);
    this.observers.forEach(obs => obs.onDataAdd(this.items.length - 1));
  }
}

@Entry
@Component
struct DataViewer {
  private source: DataSourceBase = new DataSourceBase();

  aboutToAppear() {
    for (let i = 0; i < 100; i++) {
      this.source.addItem(`項目 ${i}`);
    }
  }

  build() {
    List() {
      LazyForEach(this.source, (item: string) => {
        ListItem() {
          Text(item).fontSize(18)
            .onAppear(() => console.log(`表示: ${item}`))
        }
      }, (item: string) => item)
    }.cachedCount(5)
  }
}

ネットワークデータ実装例

class ApiDataSource extends DataSourceBase {
  private apiItems: JobData[] = [];

  addJob(data: JobData): void {
    this.apiItems.push(data);
    this.notifyDataAdd(this.apiItems.length - 1);
  }
}

async fetchRemoteData() {
  const url = 'https://api.example.com/jobs';
  const response = await fetch(url, {
    method: 'GET',
    headers: { 'Content-Type': 'application/json' }
  });

  if (response.ok) {
    const result: JobModel = await response.json();
    result.data.forEach(item => this.apiSource.addJob(item));
  }
}

@Reusable
@Component
struct JobItem {
  @Prop jobInfo: JobData

  build() {
    Row() {
      Column() {
        Text(this.jobInfo.title).fontSize(16)
        Text(this.jobInfo.company).fontColor(Color.Blue)
      }
    }
  }
}

コンポーネント再利用機構

@Reusableデコレータを子コンポーネントに適用することで、スクロール時のコンポーネントインスタンス再利用が可能になります。これにより新規オブジェクト生成オーバーヘッドが削減されます。

NodeAdapterによる最適化

List/GridコンポーネントではNodeAdapterオブジェクトを使用可能です。NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTERイベントで明示的なコンポーネント解放が必要ですが、複雑なリストでより細かい制御を実現します。

タグ: HarmonyOS ArkUI LazyForEach データ遅延ロード パフォーマンス最適化

5月29日 00:45 投稿