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