1# More Performance Improvement Methods 2 3Poor-performing code may work, but will take away from your application performance. This topic presents a line-up of recommendations that you can take to improve your implementation, thereby avoiding possible performance drop. 4 5## Using Lazy Loading 6 7When developing a long list, use of loop rendering, as in the code snippet below, can greatly slow down page loading and increase server load. 8 9```ts 10@Entry 11@Component 12struct MyComponent { 13 @State arr: number[] = Array.from(Array<number>(100), (v:number,k:number) =>k); // Construct an array of 0 to 99. 14 build() { 15 List() { 16 ForEach(this.arr, (item: number) => { 17 ListItem() { 18 Text(`item value: ${item}`) 19 } 20 }, (item: number) => item.toString()) 21 } 22 } 23} 24``` 25 26The preceding code snippet loads all of the 100 list elements at a time during page loading. This is generally not desirable. Instead, what we need is to load data from the data source and create corresponding components on demand. This can be achieved through lazy loading. The sample code is as follows: 27 28```ts 29class BasicDataSource implements IDataSource { 30 private listeners: DataChangeListener[] = []; 31 private originDataArray: string[] = []; 32 33 public totalCount(): number { 34 return 0; 35 } 36 37 public getData(index: number): string { 38 return this.originDataArray[index]; 39 } 40 41 registerDataChangeListener(listener: DataChangeListener): void { 42 if (this.listeners.indexOf(listener) < 0) { 43 console.info('add listener'); 44 this.listeners.push(listener); 45 } 46 } 47 48 unregisterDataChangeListener(listener: DataChangeListener): void { 49 const pos = this.listeners.indexOf(listener); 50 if (pos >= 0) { 51 console.info('remove listener'); 52 this.listeners.splice(pos, 1); 53 } 54 } 55 56 notifyDataReload(): void { 57 this.listeners.forEach(listener => { 58 listener.onDataReloaded(); 59 }) 60 } 61 62 notifyDataAdd(index: number): void { 63 this.listeners.forEach(listener => { 64 listener.onDataAdd(index); 65 }) 66 } 67 68 notifyDataChange(index: number): void { 69 this.listeners.forEach(listener => { 70 listener.onDataChange(index); 71 }) 72 } 73 74 notifyDataDelete(index: number): void { 75 this.listeners.forEach(listener => { 76 listener.onDataDelete(index); 77 }) 78 } 79 80 notifyDataMove(from: number, to: number): void { 81 this.listeners.forEach(listener => { 82 listener.onDataMove(from, to); 83 }) 84 } 85} 86 87class MyDataSource extends BasicDataSource { 88 private dataArray: string[] = ['item value: 0', 'item value: 1', 'item value: 2']; 89 90 public totalCount(): number { 91 return this.dataArray.length; 92 } 93 94 public getData(index: number): string { 95 return this.dataArray[index]; 96 } 97 98 public addData(index: number, data: string): void { 99 this.dataArray.splice(index, 0, data); 100 this.notifyDataAdd(index); 101 } 102 103 public pushData(data: string): void { 104 this.dataArray.push(data); 105 this.notifyDataAdd(this.dataArray.length - 1); 106 } 107} 108 109@Entry 110@Component 111struct MyComponent { 112 private data: MyDataSource = new MyDataSource(); 113 114 build() { 115 List() { 116 LazyForEach(this.data, (item: string) => { 117 ListItem() { 118 Row() { 119 Text(item).fontSize(20).margin({ left: 10 }) 120 } 121 } 122 .onClick(() => { 123 this.data.pushData('item value: ' + this.data.totalCount()); 124 }) 125 },(item:string):string => item) 126 } 127 } 128} 129``` 130 131 132 133The preceding code initializes only three list elements during page loading and loads a new list item each time a list element is clicked. 134 135## Setting Width and Height for List Components 136 137When a **List** component is nested within a **Scroll** component, all of its content will be loaded if its width and height is not specified, which may result in performance drop. 138 139> **NOTE** 140> 141> When a **List** component is nested within a **Scroll** component: 142> 143> - If the width and height of the **List** component are not set, all its child components are laid out. 144> 145> - If the width and height of the **List** component are set, only child components within its display area are laid out. 146> 147> - When [ForEach](../quick-start/arkts-rendering-control-foreach.md) is used to load child components in the **List** component, all child components are laid out, regardless of whether the width and height are set. 148> 149> - When [LazyForEach](../quick-start/arkts-rendering-control-lazyforeach.md) is used to load child components in the **List** component, all child components are laid out if the component does not have its width and height specified; and only child components within its display area are laid out if the component has its width and height specified. 150 151```ts 152class BasicDataSource implements IDataSource { 153 private listeners: DataChangeListener[] = []; 154 private originDataArray: string[] = []; 155 156 public totalCount(): number { 157 return 0; 158 } 159 160 public getData(index: number): string { 161 return this.originDataArray[index]; 162 } 163 164 registerDataChangeListener(listener: DataChangeListener): void { 165 if (this.listeners.indexOf(listener) < 0) { 166 console.info('add listener'); 167 this.listeners.push(listener); 168 } 169 } 170 171 unregisterDataChangeListener(listener: DataChangeListener): void { 172 const pos = this.listeners.indexOf(listener); 173 if (pos >= 0) { 174 console.info('remove listener'); 175 this.listeners.splice(pos, 1); 176 } 177 } 178 179 notifyDataReload(): void { 180 this.listeners.forEach(listener => { 181 listener.onDataReloaded(); 182 }) 183 } 184 185 notifyDataAdd(index: number): void { 186 this.listeners.forEach(listener => { 187 listener.onDataAdd(index); 188 }) 189 } 190 191 notifyDataChange(index: number): void { 192 this.listeners.forEach(listener => { 193 listener.onDataChange(index); 194 }) 195 } 196 197 notifyDataDelete(index: number): void { 198 this.listeners.forEach(listener => { 199 listener.onDataDelete(index); 200 }) 201 } 202 203 notifyDataMove(from: number, to: number): void { 204 this.listeners.forEach(listener => { 205 listener.onDataMove(from, to); 206 }) 207 } 208} 209 210class MyDataSource extends BasicDataSource { 211 private dataArray: Array<string> = new Array(100).fill('test'); 212 213 public totalCount(): number { 214 return this.dataArray.length; 215 } 216 217 public getData(index: number): string { 218 return this.dataArray[index]; 219 } 220 221 public addData(index: number, data: string): void { 222 this.dataArray.splice(index, 0, data); 223 this.notifyDataAdd(index); 224 } 225 226 public pushData(data: string): void { 227 this.dataArray.push(data); 228 this.notifyDataAdd(this.dataArray.length - 1); 229 } 230} 231 232@Entry 233@Component 234struct MyComponent { 235 private data: MyDataSource = new MyDataSource(); 236 237 build() { 238 Scroll() { 239 List() { 240 LazyForEach(this.data, (item: string, index: number ) => { 241 ListItem() { 242 Row() { 243 Text('item value: ' + item + (index + 1)).fontSize(20).margin(10) 244 } 245 } 246 }) 247 } 248 } 249 } 250} 251``` 252 253In the above scenario, you are advised to set the width and height for the **List** component. 254 255```ts 256class BasicDataSource implements IDataSource { 257 private listeners: DataChangeListener[] = []; 258 private originDataArray: string[] = []; 259 260 public totalCount(): number { 261 return 0; 262 } 263 264 public getData(index: number): string { 265 return this.originDataArray[index]; 266 } 267 268 registerDataChangeListener(listener: DataChangeListener): void { 269 if (this.listeners.indexOf(listener) < 0) { 270 console.info('add listener'); 271 this.listeners.push(listener); 272 } 273 } 274 275 unregisterDataChangeListener(listener: DataChangeListener): void { 276 const pos = this.listeners.indexOf(listener); 277 if (pos >= 0) { 278 console.info('remove listener') 279 this.listeners.splice(pos, 1); 280 } 281 } 282 283 notifyDataReload(): void { 284 this.listeners.forEach(listener => { 285 listener.onDataReloaded(); 286 }) 287 } 288 289 notifyDataAdd(index: number): void { 290 this.listeners.forEach(listener => { 291 listener.onDataAdd(index); 292 }) 293 } 294 295 notifyDataChange(index: number): void { 296 this.listeners.forEach(listener => { 297 listener.onDataChange(index); 298 }) 299 } 300 301 notifyDataDelete(index: number): void { 302 this.listeners.forEach(listener => { 303 listener.onDataDelete(index); 304 }) 305 } 306 307 notifyDataMove(from: number, to: number): void { 308 this.listeners.forEach(listener => { 309 listener.onDataMove(from, to); 310 }) 311 } 312} 313 314class MyDataSource extends BasicDataSource { 315 private dataArray: Array<string> = new Array(100).fill('test') 316 317 public totalCount(): number { 318 return this.dataArray.length; 319 } 320 321 public getData(index: number): string { 322 return this.dataArray[index]; 323 } 324 325 public addData(index: number, data: string): void { 326 this.dataArray.splice(index, 0, data); 327 this.notifyDataAdd(index); 328 } 329 330 public pushData(data: string): void { 331 this.dataArray.push(data); 332 this.notifyDataAdd(this.dataArray.length - 1); 333 } 334} 335 336@Entry 337@Component 338struct MyComponent { 339 private data: MyDataSource = new MyDataSource(); 340 341 build() { 342 Scroll() { 343 List() { 344 LazyForEach(this.data, (item: string, index: number) => { 345 ListItem() { 346 Text('item value: ' + item + (index + 1)).fontSize(20).margin(10) 347 }.width('100%') 348 }) 349 }.width('100%').height(500) 350 }.backgroundColor(Color.Pink) 351 } 352} 353``` 354 355 356 357## Prioritizing Conditional Rendering over Visibility Control 358 359Use of the visibility attribute to hide or show a component, as in the code snippet below, results in re-creation of the component, leading to performance drop. 360 361```ts 362@Entry 363@Component 364struct MyComponent { 365 @State isVisible: Visibility = Visibility.Visible; 366 367 build() { 368 Column() { 369 Button ("Show/Hide") 370 .onClick(() => { 371 if (this.isVisible == Visibility.Visible) { 372 this.isVisible = Visibility.None 373 } else { 374 this.isVisible = Visibility.Visible 375 } 376 }) 377 Row().visibility(this.isVisible) 378 .width(300).height(300).backgroundColor(Color.Pink) 379 }.width('100%') 380 } 381} 382``` 383 384To avoid the preceding issue, use the **if** statement instead. The sample code is as follows: 385 386```ts 387@Entry 388@Component 389struct MyComponent { 390 @State isVisible: boolean = true; 391 392 build() { 393 Column() { 394 Button ("Show/Hide") 395 .onClick(() => { 396 this.isVisible = !this.isVisible 397 }) 398 if (this.isVisible) { 399 Row() 400 .width(300).height(300).backgroundColor(Color.Pink) 401 } 402 }.width('100%') 403 } 404} 405``` 406 407 408 409## Prioritizing Flex over Column/Row 410 411By default, the flex container needs to re-lay out flex items to comply with the **flexShrink** and **flexGrow** settings. This may result in drop in rendering performance. 412 413```ts 414@Entry 415@Component 416struct MyComponent { 417 build() { 418 Flex({ direction: FlexDirection.Column }) { 419 Flex().width(300).height(200).backgroundColor(Color.Pink) 420 Flex().width(300).height(200).backgroundColor(Color.Yellow) 421 Flex().width(300).height(200).backgroundColor(Color.Grey) 422 } 423 } 424} 425``` 426 427To avoid the preceding issue, replace **Flex** with **Column** and **Row**, which can create the same page layout as **Flex** does. 428 429```ts 430@Entry 431@Component 432struct MyComponent { 433 build() { 434 Column() { 435 Row().width(300).height(200).backgroundColor(Color.Pink) 436 Row().width(300).height(200).backgroundColor(Color.Yellow) 437 Row().width(300).height(200).backgroundColor(Color.Grey) 438 } 439 } 440} 441``` 442 443 444 445## Minimizing White Blocks During Swiping 446 447To minimize white blocks during swiping, expand the UI loading range by increasing the value of **cachedCount** for the **List** and **Grid** components. **cachedCount** indicates the number of list or grid items preloaded outside of the screen. 448If an item needs to request an online image, set **cachedCount** as appropriate so that the image is downloaded in advance before the item comes into view on the screen, thereby reducing the number of white blocks. 449The following is an example of using **cachedCount**: 450 451```ts 452@Entry 453@Component 454struct MyComponent { 455 private source: MyDataSource = new MyDataSource(); 456 457 build() { 458 List() { 459 LazyForEach(this.source, (item:string) => { 460 ListItem() { 461 Text("Hello" + item) 462 .fontSize(50) 463 .onAppear(() => { 464 console.log("appear:" + item) 465 }) 466 } 467 }) 468 }.cachedCount(3) // Increase the number of list or grid items preloaded outside of the screen. 469 } 470} 471 472class MyDataSource implements IDataSource { 473 data: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 474 475 public totalCount(): number { 476 return this.data.length 477 } 478 479 public getData(index: number): number { 480 return this.data[index] 481 } 482 483 registerDataChangeListener(listener: DataChangeListener): void { 484 } 485 486 unregisterDataChangeListener(listener: DataChangeListener): void { 487 } 488} 489``` 490 491 492**Instructions** 493A greater **cachedCount** value may result in higher CPU and memory overhead of the UI. Adjust the value by taking into account both the comprehensive performance and user experience. 494