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.