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 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 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 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 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 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