1# Recommendations for Improving Performance 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(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 32 public totalCount(): number { 33 return 0 34 } 35 36 public getData(index: number): number { 37 return undefined|index 38 } 39 40 registerDataChangeListener(listener: DataChangeListener): void { 41 if (this.listeners.indexOf(listener) < 0) { 42 console.info('add listener') 43 this.listeners.push(listener) 44 } 45 } 46 47 unregisterDataChangeListener(listener: DataChangeListener): void { 48 const pos = this.listeners.indexOf(listener); 49 if (pos >= 0) { 50 console.info('remove listener') 51 this.listeners.splice(pos, 1) 52 } 53 } 54 55 notifyDataReload(): void { 56 this.listeners.forEach(listener => { 57 listener.onDataReloaded() 58 }) 59 } 60 61 notifyDataAdd(index: number): void { 62 this.listeners.forEach(listener => { 63 listener.onDataAdd(index) 64 }) 65 } 66 67 notifyDataChange(index: number): void { 68 this.listeners.forEach(listener => { 69 listener.onDataChange(index) 70 }) 71 } 72 73 notifyDataDelete(index: number): void { 74 this.listeners.forEach(listener => { 75 listener.onDataDelete(index) 76 }) 77 } 78 79 notifyDataMove(from: number, to: number): void { 80 this.listeners.forEach(listener => { 81 listener.onDataMove(from, to) 82 }) 83 } 84} 85 86class MyDataSource extends BasicDataSource { 87 private dataArray: string[] = ['item value: 0', 'item value: 1', 'item value: 2'] 88 89 public totalCount(): number { 90 return this.dataArray.length 91 } 92 93 public getData(index: number): number { 94 return this.dataArray[index] 95 } 96 97 public addData(index: number, data: string): void { 98 this.dataArray.splice(index, 0, data) 99 this.notifyDataAdd(index) 100 } 101 102 public pushData(data: string): void { 103 this.dataArray.push(data) 104 this.notifyDataAdd(this.dataArray.length - 1) 105 } 106} 107 108@Entry 109@Component 110struct MyComponent { 111 private data: MyDataSource = new MyDataSource() 112 113 build() { 114 List() { 115 LazyForEach(this.data, (item: string) => { 116 ListItem() { 117 Row() { 118 Text(item).fontSize(20).margin({ left: 10 }) 119 } 120 } 121 .onClick(() => { 122 this.data.pushData('item value: ' + this.data.totalCount()) 123 }) 124 }, ((item:string):string => item)) 125 } 126 } 127} 128``` 129 130 131 132The preceding code initializes only three list elements during page loading and loads a new list item each time a list element is clicked. 133 134## Setting Width and Height for \<List> Components 135 136When 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. 137 138> **NOTE** 139> 140> When a **\<List>** component is nested within a **\<Scroll>** component: 141> 142> - If the width and height of the **\<List>** component are not set, all its child components are laid out. 143> 144> - If the width and height of the **\<List>** component are set, only child components within its display area are laid out. 145> 146> - 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. 147> 148> - 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. 149 150```ts 151class BasicDataSource implements IDataSource { 152 private listeners: DataChangeListener[] = [] 153 154 public totalCount(): number { 155 return 0 156 } 157 158 public getData(index: number): number { 159 return index 160 } 161 162 registerDataChangeListener(listener: DataChangeListener): void { 163 if (this.listeners.indexOf(listener) < 0) { 164 console.info('add listener') 165 this.listeners.push(listener) 166 } 167 } 168 169 unregisterDataChangeListener(listener: DataChangeListener): void { 170 const pos = this.listeners.indexOf(listener); 171 if (pos >= 0) { 172 console.info('remove listener') 173 this.listeners.splice(pos, 1) 174 } 175 } 176 177 notifyDataReload(): void { 178 this.listeners.forEach(listener => { 179 listener.onDataReloaded() 180 }) 181 } 182 183 notifyDataAdd(index: number): void { 184 this.listeners.forEach(listener => { 185 listener.onDataAdd(index) 186 }) 187 } 188 189 notifyDataChange(index: number): void { 190 this.listeners.forEach(listener => { 191 listener.onDataChange(index) 192 }) 193 } 194 195 notifyDataDelete(index: number): void { 196 this.listeners.forEach(listener => { 197 listener.onDataDelete(index) 198 }) 199 } 200 201 notifyDataMove(from: number, to: number): void { 202 this.listeners.forEach(listener => { 203 listener.onDataMove(from, to) 204 }) 205 } 206} 207 208class MyDataSource extends BasicDataSource { 209 private dataArray: Array<string> = new Array(100).fill('test') 210 211 public totalCount(): number { 212 return this.dataArray.length 213 } 214 215 public getData(index: number): number { 216 return this.dataArray[index] 217 } 218 219 public addData(index: number, data: string): void { 220 this.dataArray.splice(index, 0, data) 221 this.notifyDataAdd(index) 222 } 223 224 public pushData(data: string): void { 225 this.dataArray.push(data) 226 this.notifyDataAdd(this.dataArray.length - 1) 227 } 228} 229 230@Entry 231@Component 232struct MyComponent { 233 private data: MyDataSource = new MyDataSource() 234 235 build() { 236 Scroll() { 237 List() { 238 LazyForEach(this.data, (item: string, index?: number|undefined) => { 239 ListItem() { 240 Row() { 241 if(index){ 242 Text('item value: ' + item + (index + 1)).fontSize(20).margin(10) 243 } 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 259 public totalCount(): number { 260 return 0 261 } 262 263 public getData(index: number): number { 264 return index 265 } 266 267 registerDataChangeListener(listener: DataChangeListener): void { 268 if (this.listeners.indexOf(listener) < 0) { 269 console.info('add listener') 270 this.listeners.push(listener) 271 } 272 } 273 274 unregisterDataChangeListener(listener: DataChangeListener): void { 275 const pos = this.listeners.indexOf(listener); 276 if (pos >= 0) { 277 console.info('remove listener') 278 this.listeners.splice(pos, 1) 279 } 280 } 281 282 notifyDataReload(): void { 283 this.listeners.forEach(listener => { 284 listener.onDataReloaded() 285 }) 286 } 287 288 notifyDataAdd(index: number): void { 289 this.listeners.forEach(listener => { 290 listener.onDataAdd(index) 291 }) 292 } 293 294 notifyDataChange(index: number): void { 295 this.listeners.forEach(listener => { 296 listener.onDataChange(index) 297 }) 298 } 299 300 notifyDataDelete(index: number): void { 301 this.listeners.forEach(listener => { 302 listener.onDataDelete(index) 303 }) 304 } 305 306 notifyDataMove(from: number, to: number): void { 307 this.listeners.forEach(listener => { 308 listener.onDataMove(from, to) 309 }) 310 } 311} 312 313class MyDataSource extends BasicDataSource { 314 private dataArray: Array<string> = new Array(100).fill('test') 315 316 public totalCount(): number { 317 return this.dataArray.length 318 } 319 320 public getData(index: number): number { 321 return this.dataArray[index] 322 } 323 324 public addData(index: number, data: string): void { 325 this.dataArray.splice(index, 0, data) 326 this.notifyDataAdd(index) 327 } 328 329 public pushData(data: string): void { 330 this.dataArray.push(data) 331 this.notifyDataAdd(this.dataArray.length - 1) 332 } 333} 334 335@Entry 336@Component 337struct MyComponent { 338 private data: MyDataSource = new MyDataSource() 339 340 build() { 341 Scroll() { 342 List() { 343 LazyForEach(this.data, (item: string, index?: number|undefined) => { 344 ListItem() { 345 if(index){ 346 Text('item value: ' + item + (index + 1)).fontSize(20).margin(10) 347 } 348 }.width('100%') 349 }) 350 }.width('100%').height(500) 351 }.backgroundColor(Color.Pink) 352 } 353} 354``` 355 356 357 358## Prioritizing Conditional Rendering over Visibility Control 359 360Use 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. 361 362```ts 363@Entry 364@Component 365struct MyComponent { 366 @State isVisible: Visibility = Visibility.Visible; 367 368 build() { 369 Column() { 370 Button ("Show/Hide") 371 .onClick(() => { 372 if (this.isVisible == Visibility.Visible) { 373 this.isVisible = Visibility.None 374 } else { 375 this.isVisible = Visibility.Visible 376 } 377 }) 378 Row().visibility(this.isVisible) 379 .width(300).height(300).backgroundColor(Color.Pink) 380 }.width('100%') 381 } 382} 383``` 384 385To avoid the preceding issue, use the **if** statement instead. The sample code is as follows: 386 387```ts 388@Entry 389@Component 390struct MyComponent { 391 @State isVisible: boolean = true; 392 393 build() { 394 Column() { 395 Button ("Show/Hide") 396 .onClick(() => { 397 this.isVisible = !this.isVisible 398 }) 399 if (this.isVisible) { 400 Row() 401 .width(300).height(300).backgroundColor(Color.Pink) 402 } 403 }.width('100%') 404 } 405} 406``` 407 408 409 410## Prioritizing Flex over Column/Row 411 412By 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. 413 414```ts 415@Entry 416@Component 417struct MyComponent { 418 build() { 419 Flex({ direction: FlexDirection.Column }) { 420 Flex().width(300).height(200).backgroundColor(Color.Pink) 421 Flex().width(300).height(200).backgroundColor(Color.Yellow) 422 Flex().width(300).height(200).backgroundColor(Color.Grey) 423 } 424 } 425} 426``` 427 428To avoid the preceding issue, replace **Flex** with **Column** and **Row**, which can create the same page layout as **Flex** does. 429 430```ts 431@Entry 432@Component 433struct MyComponent { 434 build() { 435 Column() { 436 Row().width(300).height(200).backgroundColor(Color.Pink) 437 Row().width(300).height(200).backgroundColor(Color.Yellow) 438 Row().width(300).height(200).backgroundColor(Color.Grey) 439 } 440 } 441} 442``` 443 444 445 446## Minimizing White Blocks During Swiping 447 448To 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. 449If 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. 450The following is an example of using **cachedCount**: 451 452```ts 453@Entry 454@Component 455struct MyComponent { 456 private source: MyDataSource = new MyDataSource(); 457 458 build() { 459 List() { 460 LazyForEach(this.source, (item:string) => { 461 ListItem() { 462 Text("Hello" + item) 463 .fontSize(50) 464 .onAppear(() => { 465 console.log("appear:" + item) 466 }) 467 } 468 }) 469 }.cachedCount(3) // Increase the number of list or grid items preloaded outside of the screen. 470 } 471} 472 473class MyDataSource implements IDataSource { 474 data: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 475 476 public totalCount(): number { 477 return this.data.length 478 } 479 480 public getData(index: number): number { 481 return this.data[index] 482 } 483 484 registerDataChangeListener(listener: DataChangeListener): void { 485 } 486 487 unregisterDataChangeListener(listener: DataChangeListener): void { 488 } 489} 490``` 491 492 493**Instructions** 494A 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. 495