一、背景:
LazyForEach从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach,框架会根据滚动容器可视区域按需创建组件,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。
二、使用场景
LazyForEach必须在容器组件内使用,仅有List、Grid、Swiper以及WaterFlow组件支持数据懒加载(可配置cachedCount属性,即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次性加载所有的数据。
长数据列表常见使用LazyForEach
三、使用语法
3.1、语法
LazyForEach(
dataSource: IDataSource,
itemGenerator: (item: any) => void,
keyGenerator?: (item: any) => string
): void
3.2、参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
dataSource | IDataSource | 是 | LazyForEach数据源,需要开发者实现相关接口。 |
itemGenerator | (item: Object, index: number) => void | 是 | 子组件生成函数,为数组中的每一个数据项创建一个子组件。 说明: - item是当前数据项,index是数据项索引值。 - itemGenerator的函数体必须使用大括号{...}。 - itemGenerator每次迭代只能并且必须生成一个子组件。 - itemGenerator中可以使用if语句,但是必须保证if语句每个分支都会创建一个相同类型的子组件。 - itemGenerator中不允许使用ForEach和LazyForEach语句。 |
keyGenerator | (item: Object, index: number) => string | 否 | 键值生成函数,用于给数据源中的每一个数据项生成唯一且固定的键值。当数据项在数组中的位置更改时,其键值不得更改,当数组中的数据项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则LazyForEach中的所有节点都将重建。 说明: - item是当前数据项,index是数据项索引值。 - 数据源中的每一个数据项生成的键值不能重复。 |
3.3、数据源dataSource的说明
3.4、DataChangeListener类型说明
interface DataChangeListener {
onDataReloaded(): void; // 重新加载数据完成后调用
onDataAdded(index: number): void; // 添加数据完成后调用
onDataMoved(from: number, to: number): void; // 数据移动起始位置与数据移动目标位置交换完成后调用
onDataDeleted(index: number): void; // 删除数据完成后调用
onDataChanged(index: number): void; // 改变数据完成后调用
onDataAdd(index: number): void; // 添加数据完成后调用
onDataMove(from: number, to: number): void; // 数据移动起始位置与数据移动目标位置交换完成后调用
onDataDelete(index: number): void; // 删除数据完成后调用
onDataChange(index: number): void; // 改变数据完成后调用
}
备注:详细API可以查看官方文档👉👉文档中心
下面主要记录下,工作中如何使用
四、使用步骤
4.1、定义类来实现IDataSource接口方法
// Basic implementation of IDataSource to handle data listener
export abstract class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = [];
public abstract totalCount(): number
public abstract getData(index: number): ESObject
// 该方法为框架侧调用,为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);
})
}
// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
})
}
// 通知LazyForEach组件需要在index对应索引处删除该子组件
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
})
}
// 通知LazyForEach组件将from索引和to索引处的子组件进行交换
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to);
})
}
}
4.2、定义真实处理数据的类
例如:添加元素、删除元素、交换元素、更新元素、重载元素、全部更新等
import { BasicDataSource } from './BasicDataSource';
export class ListDataSource<T> extends BasicDataSource {
private dataArray: T[] = [];
public totalCount(): number {
return this.dataArray.length;
}
public getData(index: number): T {
return this.dataArray[index];
}
public addData(index: number, data: T): void {
this.dataArray.splice(index, 0, data);
this.notifyDataAdd(index);
}
public pushDataList(data: T[]): void {
this.dataArray = this.dataArray.concat(data);
this.notifyDataAdd(this.dataArray.length - data.length);
}
public pushDataItem(data: T): void {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
public deleteData(index: number): void {
this.dataArray.splice(index, 1);
this.notifyDataDelete(index);
}
public moveData(from: number, to: number): void {
let temp: T = this.dataArray[from];
this.dataArray[from] = this.dataArray[to];
this.dataArray[to] = temp;
this.notifyDataMove(from, to);
}
public changeData(index: number, data: T): void {
this.dataArray.splice(index, 1, data);
this.notifyDataChange(index);
}
public refreshData(list: T[]) {
this.dataArray = list
this.notifyDataReload()
}
public reloadData(): void {
this.notifyDataReload();
}
public modifyAllData(changeItem: (item: T) => T): void {
this.dataArray = this.dataArray.map((item: T) => {
return changeItem(item);
})
}
}
4.3、组件中使用
import { MyDataSource } from '../model/ListDataSource';
@Entry
@Component
struct MyComponent {
private data: MyDataSource = new MyDataSource();
aboutToAppear() {
for (let i = 0; i <= 20; i++) {
this.data.pushData(`Hello ${i}`)
}
}
build() {
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.deleteData(this.data.dataArray.indexOf(item));
})
}, (item: string) => item)
}.cachedCount(5)
}
}