• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![LazyForEach1](figures/LazyForEach1.gif)
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![list1](figures/list1.gif)
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![isVisible](figures/isVisible.gif)
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![flex1](figures/flex1.PNG)
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![list2](figures/list2.gif)
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