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![LazyForEach1](figures/LazyForEach1.gif)
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![list1](figures/list1.gif)
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![isVisible](figures/isVisible.gif)
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![flex1](figures/flex1.PNG)
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![list2](figures/list2.gif)
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