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イベントで明示的なコンポーネント解放が必要ですが、複雑なリストでより細かい制御を実現します。