• 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## 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,k) =>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): any {
37    return undefined
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): any {
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 => 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## Prioritizing Conditional Rendering over Visibility Control
135
136Use 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.
137
138```ts
139@Entry
140@Component
141struct MyComponent {
142  @State isVisible: Visibility = Visibility.Visible;
143
144  build() {
145    Column() {
146      Button ("Show/Hide")
147        .onClick(() => {
148          if (this.isVisible == Visibility.Visible) {
149            this.isVisible = Visibility.None
150          } else {
151            this.isVisible = Visibility.Visible
152          }
153        })
154      Row().visibility(this.isVisible)
155        .width(300).height(300).backgroundColor(Color.Pink)
156    }.width('100%')
157  }
158}
159```
160
161To avoid the preceding issue, use the **if** statement instead. The sample code is as follows:
162
163```ts
164@Entry
165@Component
166struct MyComponent {
167  @State isVisible: boolean = true;
168
169  build() {
170    Column() {
171      Button ("Show/Hide")
172        .onClick(() => {
173          this.isVisible = !this.isVisible
174        })
175      if (this.isVisible) {
176        Row()
177          .width(300).height(300).backgroundColor(Color.Pink)
178      }
179    }.width('100%')
180  }
181}
182```
183
184![isVisible](figures/isVisible.gif)
185
186## Prioritizing Flex over Column/Row
187
188By 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.
189
190```ts
191@Entry
192@Component
193struct MyComponent {
194  build() {
195    Flex({ direction: FlexDirection.Column }) {
196      Flex().width(300).height(200).backgroundColor(Color.Pink)
197      Flex().width(300).height(200).backgroundColor(Color.Yellow)
198      Flex().width(300).height(200).backgroundColor(Color.Grey)
199    }
200  }
201}
202```
203
204To avoid the preceding issue, replace **Flex** with **Column** and **Row**, which can create the same page layout as **Flex** does.
205
206```ts
207@Entry
208@Component
209struct MyComponent {
210  build() {
211    Column() {
212      Row().width(300).height(200).backgroundColor(Color.Pink)
213      Row().width(300).height(200).backgroundColor(Color.Yellow)
214      Row().width(300).height(200).backgroundColor(Color.Grey)
215    }
216  }
217}
218```
219
220![flex1](figures/flex1.PNG)
221
222## Setting Width and Height for \<List> Components
223
224When 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.
225
226```ts
227@Entry
228@Component
229struct MyComponent {
230  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
231
232  build() {
233    Scroll() {
234      List() {
235        ForEach(this.arr, (item) => {
236          ListItem() {
237            Text(`item value: ${item}`).fontSize(30).margin({ left: 10 })
238          }.height(100)
239        }, (item) => item.toString())
240      }
241    }.backgroundColor(Color.Pink)
242  }
243}
244```
245
246Therefore, in the above scenario, you are advised to set the width and height for the **\<List>** component as follows:
247
248```ts
249@Entry
250@Component
251struct MyComponent {
252  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
253
254  build() {
255    Scroll() {
256      List() {
257        ForEach(this.arr, (item) => {
258          ListItem() {
259            Text(`item value: ${item}`).fontSize(30).margin({ left: 10 })
260          }.height(100)
261        }, (item) => item.toString())
262      }.width('100%').height(500)
263    }.backgroundColor(Color.Pink)
264  }
265}
266```
267
268![list1](figures/list1.gif)
269
270## Minimizing White Blocks During Swiping
271
272To 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.
273If 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.
274The following is an example of using **cachedCount**:
275
276```ts
277@Entry
278@Component
279struct MyComponent {
280  private source: MyDataSource = new MyDataSource();
281
282  build() {
283    List() {
284      LazyForEach(this.source, item => {
285        ListItem() {
286          Text("Hello" + item)
287            .fontSize(50)
288            .onAppear(() => {
289              console.log("appear:" + item)
290            })
291        }
292      })
293    }.cachedCount(3) // Increase the number of list or grid items preloaded outside of the screen.
294  }
295}
296
297class MyDataSource implements IDataSource {
298  data: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
299
300  public totalCount(): number {
301    return this.data.length
302  }
303
304  public getData(index: number): any {
305    return this.data[index]
306  }
307
308  registerDataChangeListener(listener: DataChangeListener): void {
309  }
310
311  unregisterDataChangeListener(listener: DataChangeListener): void {
312  }
313}
314```
315![list2](figures/list2.gif)
316
317**Instructions**
318A 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.
319