1# 数据懒加载 2 3开发框架提供数据懒加载(LazyForEach组件)从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。LazyForEach定义如下: 4 5 6``` 7LazyForEach( 8 dataSource: IDataSource, // Data source to be iterated 9 itemGenerator: (item: any) => void, // child component generator 10 keyGenerator?: (item: any) => string // (optional) Unique key generator, which is recommended. 11): void 12 13interface IDataSource { 14 totalCount(): number; // Get total count of data 15 getData(index: number): any; // Get single data by index 16 registerDataChangeListener(listener: DataChangeListener): void; // Register listener to listening data changes 17 unregisterDataChangeListener(listener: DataChangeListener): void; // Unregister listener 18} 19 20interface DataChangeListener { 21 onDataReloaded(): void; // Called while data reloaded 22 onDataAdd(index: number): void; // Called while single data added 23 onDataMove(from: number, to: number): void; // Called while single data moved 24 onDataDelete(index: number): void; // Called while single data deleted 25 onDataChange(index: number): void; // Called while single data changed 26} 27``` 28 29 30## 接口 31 32 33### LazyForEach 34 35LazyForEach(dataSource: IDataSource, itemGenerator: (item: any) => void, keyGenerator?: (item: any) => string):void 36 37表1 参数说明 38 39| 参数名 | 参数类型 | 必填 | 默认值 | 参数描述 | 40| -------- | -------- | -------- | -------- | -------- | 41| dataSource | IDataSource | 是 | - | 实现IDataSource接口的对象,需要开发者实现相关接口。 | 42| itemGenerator | (item: any) => void | 是 | - | 生成子组件的lambda函数,为给定数组项生成一个或多个子组件,单个组件和子组件列表必须括在大括号“{....}”中。 | 43| keyGenerator | (item: any) => string | 否 | - | 匿名函数,用于键值生成,为给定数组项生成唯一且稳定的键值。当子项在数组中的位置更改时,子项的键值不得更改,当数组中的子项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则LazyForEach中的所有节点都将重建。 | 44 45 46表2 IDataSource类型说明 47 48| 名称 | 描述 | 49| -------- | -------- | 50| totalCount(): number | 获取数据总数。 | 51| getData(index: number): any | 获取索引对应的数据。 | 52| registerDataChangeListener(listener:DataChangeListener): void | 注册改变数据的控制器。 | 53| unregisterDataChangeListener(listener:DataChangeListener): void | 注销改变数据的控制器。 | 54 55 56表3 DataChangeListener类型说明 57 58| 名称 | 描述 | 59| -------- | -------- | 60| onDataReloaded(): void | 重新加载所有数据。 | 61| onDataAdded(index: number): void <sup>(deprecated) </sup>| 通知组件index的位置有数据添加。 | 62| onDataMoved(from: number, to: number): void <sup>(deprecated) </sup>| 通知组件数据从from的位置移到to的位置。 | 63| onDataDeleted(index: number): void <sup>(deprecated) </sup>| 通知组件index的位置有数据删除。 | 64| onDataChanged(index: number): void <sup>(deprecated) </sup>| 通知组件index的位置有数据变化。 | 65| onDataAdd(index: number): void <sup>8+</sup> | 通知组件index的位置有数据添加。 | 66| onDataMove(from: number, to: number): void <sup>8+</sup> | 通知组件数据从from的位置移到to的位置。 | 67| onDataDelete(index: number): void <sup>8+</sup> | 通知组件index的位置有数据删除。 | 68| onDataChange(index: number): void <sup>8+</sup> | 通知组件index的位置有数据变化。 | 69 70 71>  **说明:** 72> - 数据懒加载必须在容器组件内使用,且仅有List、Grid以及Swiper组件支持数据的懒加载(即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次加载所有的数据; 73> 74> - LazyForEach在每次迭代中,必须且只允许创建一个子组件; 75> 76> - 生成的子组件必须允许在LazyForEach的父容器组件中; 77> 78> - 允许LazyForEach包含在if/else条件渲染语句中,不允许LazyForEach中出现if/else条件渲染语句; 79> 80> - 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,仅itemGenerator中的UI描述的组件内使用了状态变量时,才会触发组件刷新; 81> 82> - 子项生成器函数的调用顺序不一定和数据源中的数据项相同,在开发过程中不要假设子项生成器和键值生成器函数是否执行以及执行顺序。如下示例可能无法正常工作: 83> ``` 84> LazyForEach(dataSource, item => {Text(`${++counter}. item.label`)}) 85> ``` 86> 87> 正确的示例如下: 88> 89> ``` 90> LazyForEach(dataSource, 91> item => Text(`${item.i}. item.data.label`)), 92> item => item.data.id.toString()) 93> ``` 94 95 96## 示例 97 98``` 99// Basic implementation of IDataSource to handle data listener 100class BasicDataSource implements IDataSource { 101 private listeners: DataChangeListener[] = [] 102 103 public totalCount(): number { 104 return 0 105 } 106 public getData(index: number): any { 107 return undefined 108 } 109 110 registerDataChangeListener(listener: DataChangeListener): void { 111 if (this.listeners.indexOf(listener) < 0) { 112 console.info('add listener') 113 this.listeners.push(listener) 114 } 115 } 116 unregisterDataChangeListener(listener: DataChangeListener): void { 117 const pos = this.listeners.indexOf(listener); 118 if (pos >= 0) { 119 console.info('remove listener') 120 this.listeners.splice(pos, 1) 121 } 122 } 123 124 notifyDataReload(): void { 125 this.listeners.forEach(listener => { 126 listener.onDataReloaded() 127 }) 128 } 129 notifyDataAdd(index: number): void { 130 this.listeners.forEach(listener => { 131 listener.onDataAdd(index) 132 }) 133 } 134 notifyDataChange(index: number): void { 135 this.listeners.forEach(listener => { 136 listener.onDataChange(index) 137 }) 138 } 139 notifyDataDelete(index: number): void { 140 this.listeners.forEach(listener => { 141 listener.onDataDelete(index) 142 }) 143 } 144 notifyDataMove(from: number, to: number): void { 145 this.listeners.forEach(listener => { 146 listener.onDataMove(from, to) 147 }) 148 } 149} 150 151class MyDataSource extends BasicDataSource { 152 private dataArray: string[] = ['/path/image0', '/path/image1', '/path/image2', '/path/image3'] 153 154 public totalCount(): number { 155 return this.dataArray.length 156 } 157 public getData(index: number): any { 158 return this.dataArray[index] 159 } 160 161 public addData(index: number, data: string): void { 162 this.dataArray.splice(index, 0, data) 163 this.notifyDataAdd(index) 164 } 165 public pushData(data: string): void { 166 this.dataArray.push(data) 167 this.notifyDataAdd(this.dataArray.length - 1) 168 } 169} 170 171@Entry 172@Component 173struct MyComponent { 174 private data: MyDataSource = new MyDataSource() 175 build() { 176 List({space: 3}) { 177 LazyForEach(this.data, (item: string) => { 178 ListItem() { 179 Row() { 180 Image(item).width("30%").height(50) 181 Text(item).fontSize(20).margin({left:10}) 182 }.margin({left: 10, right: 10}) 183 } 184 .onClick(()=>{ 185 this.data.pushData('/path/image' + this.data.totalCount()) 186 }) 187 }, item => item) 188 } 189 } 190} 191``` 192