1# 应用性能优化常见问题解决指导 2 3## 概述 4 5本文总结了实际开发应用时常见的性能优化规范,配合举例实际开发中常见的正反例代码,帮助开发者解决大部分性能问题。 6 7### 性能规范总览目录 8|              <br />分类<br />                 |<br />高频程度 (5满分)<br />     | 规范(检查项) | 实操方法 | <br />代码示例<br />     | 9|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------:|:---------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------:| 10| 响应时延 / 完成时延 | 5 | 不建议在aboutToAppear(),aboutToDisappear()等生命周期中执行耗时操作 | 排查所有的aboutToAppear和aboutToDisappear函数(或者通过Trace查看),查看是否有耗时操作,改为setTimeOut或者在TaskPool中执行。 | [代码示例](#不建议在abouttoappearabouttodisappear等生命周期中执行耗时操作) | 11| 响应时延 / 完成时延 | 5 | 不要在回调函数中执行耗时操作(ArkUI接口回调、网络访问回调、await等) | 排查所有的回调函数(或者通过Trace查看),尤其是ArkUI接口,网络回调函数,查看是否有耗时操作,是否使用了await操作,改为setTimeOut或者在TaskPool中执行。 | [代码示例](#不要在回调函数中执行耗时操作arkui接口回调网络访问回调await等) | 12| 响应时延 / 完成时延 / 帧率 | 5 | 列表场景未使用LazyForEach+组件复用+缓存列表项 | 排查使用LazyForEach的代码,确认是否有使用组件复用(@Reusable)+缓存列表项(cachedCount)。 | [代码示例](#列表场景未使用lazyforeach组件复用缓存列表项) | 13| 完成时延 | 5 | Web未使用预连接,未提前初始化引擎 | 在应用创建Ability的时候,在OnCreate阶段预先初始化内核,建议把引擎的初始化放在setTimeOut中。 | [代码示例](#web未使用预连接未提前初始化引擎) | 14| 响应时延 / 完成时延 | 5 | 高频接口中不要打印Trace和日志 | 排查接口onTouch、onItemDragMove、onDragMove、onDidScroll、onMouse、onVisibleAreaChange、OnAreaChange、onActionUpdate、animator的onFrame、组件复用场景下的aboutToReuse,不建议在里面打印trace和日志。 | [代码示例](#高频接口中不要打印trace和日志) | 15| 完成时延 / 帧率 | 4 | 组件复用里面有if语句,但是未使用reuseId | 排查使用了@Reusable的自定义组件,查看build中给是否使用了if/else或ForEach等条件渲染语句,如果使用了,需要配合reuseId一起使用。 | [代码示例](#组件复用里面有if语句但是未使用reuseid) | 16| 响应时延 / 完成时延 | 4 | 不建议使用@Prop装饰器 | 全局搜索@Prop并且替换 | [代码示例](#不建议使用prop装饰器) | 17| 响应时延 / 完成时延 | 3 | 避免在ResourceManager的getXXXSync接口入参中直接使用资源信息 | 排查ResourceManager.getXXXSync接口,查看入参时需要使用getStringSync($r('app.media.icon').id)的形式,如果未使用需要整改。 | [代码示例](#避免在resourcemanager的getxxxsync接口入参中直接使用资源信息) | 18| 响应时延 / 完成时延 | 3 | 展示用的自定义组件(数据从父组件中获取,无独立数据处理)使用@Builder替换 | 审视@Component标记的自定义组件,如果里面没有独立的生命周期处理逻辑,数据由父组件传递,建议@Builder替代。 | [代码示例](#展示用的自定义组件数据从父组件中获取无独立数据处理使用builder替换) | 19| 响应时延 / 完成时延 / 帧率 | 3 | 删除无具体逻辑的生命周期,ArkUI的函数回调等,删除冗余堵塞日志打印 | 排查所有的aboutToAppear、aboutToDisappear等生命周期函数,排查ArkUI的回调函数,如果函数中无具体业务逻辑,例如只打印了日志,删除函数回调。 | [代码示例](#删除无具体逻辑的生命周期arkui的函数回调等删除冗余堵塞日志打印) | 20| 响应时延 / 完成时延 | 3 | 删除未关联组件的状态变量装饰器 | 排查全局的状态变量装饰器,如果变量未关联组件,删除装饰器。 | [代码示例](#删除未关联组件的状态变量装饰器) | 21| 帧率 | 2 | crypto-js性能差 | 排查buffer.from关键字,加密建议使用原生的cryptoFramework,然后将buffer替换为base64helper,性能提升10倍以上, 且数据量越大越明显。 | [代码示例](#crypto-js性能差) | 22| 响应时延 / 完成时延 | 1 | 不建议使用Marquee组件 | 排查Marquee关键字,使用Text的跑马灯模式(TextOverflow.MARQUEE)替代。 | [代码示例](#不建议使用marquee组件) | 23| 完成时延 | 1 | 不能使用函数作为ArkUI组件的属性和组件复用的自定义组件的入参 | 查看属性是否有xx()函数写法,确认函数/方法中是否有耗时操作,替换成变量。 | [代码示例](#不能使用函数作为arkui组件的属性和组件复用的自定义组件的入参) | 24| 完成时延 | 1 | 不建议使用.linearGradient颜色渐变属性 | 排查linearGradient关键字,可以使用图片代替。 | [代码示例](#不建议使用lineargradient颜色渐变属性) | 25| 完成时延 / 帧率 | 1 | 不要在for/while循环中执行耗时操作 | 排查for/while循环,查看里面是否有打印日志或者Trace。 | [代码示例](#不要在forwhile循环中执行耗时操作) | 26| 完成时延 / 帧率 | 1 | 变量初值不建议设置为undefined,需进行默认初始化 | 例如number设置为0,string设置为空字符串等,这样在使用过程中更不需要增加额外判空。排查类中的变量,看看是否有初始化为undefined。 | [代码示例](#变量初值不建议设置为undefined需进行默认初始化) | 27 28## 性能优化规范 29 30### 不建议在aboutToAppear()、aboutToDisappear()等生命周期中执行耗时操作 31#### 类型 32响应时延/完成时延 33#### 解决方法 34排查所有的aboutToAppear和aboutToDisappear函数(或者通过Trace查看),查看是否有耗时操作,改为setTimeOut或者在TaskPool中执行。 35 36#### 反例 37```typescript 38@Entry 39@Component 40struct ViewA { 41 @State private text: string = ""; 42 private count: number = 0; 43 // 反例:在aboutToAppear接口中执行耗时操作,阻塞页面绘制。 44 aboutToAppear() { 45 // 耗时操作 46 this.computeTask(); 47 let context = context.resourceManager.getStringSync($r('app.string.startup_text')); 48 } 49 50 computeTask(): void { 51 this.count = 0; 52 while (this.count < LARGE_NUMBER) { 53 this.count++; 54 } 55 let context = getContext(this) as Context; 56 this.text = context.resourceManager.getStringSync($r('app.string.task_text')); 57} 58} 59``` 60#### 正例 61```typescript 62@Entry 63@Component 64struct ViewB { 65 @State private text: string = ""; 66 private count: number = 0; 67 private readonly DELAYED_TIME: number = 2000; // 定时器设置延时2s 68 69 // 正例:在aboutToAppear接口中对耗时间的计算任务进行了异步处理。 70 aboutToAppear() { 71 // 耗时操作 72 this.computeTaskAsync(); // 异步任务 73 let context = getContext(this) as Context; 74 this.text = context.resourceManager.getStringSync($r('app.string.startup_text')); 75 } 76 77 computeTask(): void { 78 this.count = 0; 79 while (this.count < LARGE_NUMBER) { 80 this.count++; 81 } 82 let context = getContext(this) as Context; 83 this.text = context.resourceManager.getStringSync($r('app.string.task_text')); 84} 85 86// 运算任务异步处理 87private computeTaskAsync(): void { 88 setTimeout(() => { 89 // 这里使用setTimeout来实现异步延迟运行 90 this.computeTask(); 91 }, DELAYED_TIME) 92} 93} 94``` 95#### 高频程度&收益(5满分) 965 97 98### 不要在回调函数中执行耗时操作(ArkUI接口回调、网络访问回调、await等) 99#### 类型 100响应时延/完成时延 101#### 解决方法 102排查所有的回调函数(或者通过Trace查看),尤其是ArkUI接口,网络回调函数,查看是否有耗时操作,是否使用了await操作,改为setTimeOut或者在TaskPool中执行。 103#### 反例 104```typescript 105import http from '@ohos.net.http'; 106 107async aboutToAppear() { 108 // ... 109 const b = await http.createHttp(); 110} 111``` 112#### 正例 113```typescript 114aboutToAppear() { 115 // ... 116 // 在生命周期中,使用TaskPool加载和解析网络数据 117 this.requestByTaskPool(); 118} 119 120@Concurrent 121getInfoFromHttp(): string[] { 122 // 从网络加载数据 123 return http.request(); 124} 125 126requestByTaskPool(): void { 127 // 创建任务项 128 let task: taskpool.Task = new taskpool.Task(this.getInfoFromHttp); 129 try { 130 // 执行网络加载函数 131 taskpool.execute(task, taskpool.Priority.HIGH).then((res: string[]) => { 132}); 133} catch (err) { 134 logger.error(TAG, "failed, " + (err as BusinessError).toString()); 135} 136} 137``` 138#### 高频程度&收益(5满分) 1395 140 141### 列表场景未使用LazyForEach+组件复用+缓存列表项 142#### 类型 143响应时延/完成时延/帧率 144#### 解决方法 145排查使用LazyForEach的代码,确认是否有使用组件复用(@Reusable)+缓存列表项(cachedCount)。 146#### 反例 147```typescript 148struct GoodView { 149 build() { 150 Grid() { 151 // 未使用LazyForEach+组件复用+缓存列表项 152 ForEach(this.GoodDataOne, (item, index) => { 153 GridItem() { 154 Column() { 155 Image(item.img) 156 .height(item.hei) 157 .width('100%') 158 .objectFit(ImageFit.Fill) 159 160 Text(item.introduce) 161 .fontSize(14) 162 .padding({ left: 5, right: 5 }) 163 .margin({ top: 5 }) 164 Row() { 165 Row() { 166 Text('¥') 167 .fontSize(10) 168 .fontColor(Color.Red) 169 .baselineOffset(-4) 170 Text(item.price) 171 .fontSize(16) 172 .fontColor(Color.Red) 173 Text(item.numb) 174 .fontSize(10) 175 .fontColor(Color.Gray) 176 .baselineOffset(-4) 177 .margin({ left: 5 }) 178 } 179 180 Image($r('app.media.photo63')) 181 .width(20) 182 .height(10) 183 .margin({ bottom: -8 }) 184 } 185 .width('100%') 186 .justifyContent(FlexAlign.SpaceBetween) 187 .padding({ left: 5, right: 5 }) 188 .margin({ top: 15 }) 189 } 190 .borderRadius(10) 191 .backgroundColor(Color.White) 192 .clip(true) 193 .width('100%') 194 .height(290) 195 } 196 }, (item) => JSON.stringify(item)) 197 } 198 } 199} 200``` 201#### 正例 202```typescript 203// 组件复用 204@Reusable 205@Component 206struct GoodItems { 207 @State img: Resource = $r("app.media.photo61"); 208 @State webImg?: string = ''; 209 @State hei: number = 0; 210 @State introduce: string = ''; 211 @State price: string = ''; 212 @State numb: string = ''; 213 @LocalStorageLink('storageSimpleProp') simpleVarName: string = ''; 214 isOnclick: boolean = true; 215 index: number = 0; 216 controllerVideo: VideoController = new VideoController(); 217 218 aboutToReuse(params) 219 { 220 this.webImg = params.webImg; 221 this.img = params.img; 222 this.hei = params.hei; 223 this.introduce = params.introduce; 224 this.price = params.price; 225 this.numb = params.numb; 226 } 227 228 build() { 229 Grid(){ 230 // 懒加载 231 LazyForEach(this.GoodDataOne, (item, index) => { 232 GridItem() { 233 GoodItems({ 234 isOnclick:item.data.isOnclick, 235 img:item.data.img, 236 webImg:item.data.webImg, 237 hei:item.data.hei, 238 introduce:item.data.introduce, 239 price:item.data.price, 240 numb:item.data.numb, 241 index:index 242 }) 243 .reuseId(this.CombineStr(item.type)) 244 } 245 }, (item) => JSON.stringify(item)) 246 }.cachedCount(2) // 缓存列表项 247 } 248} 249``` 250#### 高频程度&收益(5满分) 2515 252 253### Web未使用预连接,未提前初始化引擎 254#### 类型 255完成时延 256#### 解决方法 257在应用创建Ability的时候,在OnCreate阶段预先初始化内核,建议把引擎的初始化放在setTimeOut中。 258#### 反例 259```typescript 260// Web组件引擎没有初始化,且沒有使用预连接 261export default class EntryAbility extends UIAbility { 262 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 263 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 264 } 265} 266controller: webview.WebviewController = new webview.WebviewController(); 267// ... 268Web({ src: 'https://www.example.com', controller: this.controller }) 269 270``` 271#### 正例 272```typescript 273export default class EntryAbility extends UIAbility { 274 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 275 console.info("EntryAbility onCreate") 276 // 在 Web 组件初始化之前,通过此接口加载 Web 引擎的动态库文件,以提高启动性能。 277 setTimeout(() => { 278 // 这里使用setTimeout来实现延迟运行 279 web_webview.WebviewController.initializeWebEngine() 280 }, 200) 281 console.info("EntryAbility onCreate done"); 282 } 283} 284 285controller: webview.WebviewController = new webview.WebviewController(); 286// ... 287Web({ src: 'https://www.example.com', controller: this.controller }) 288 289``` 290#### 高频程度&收益(5满分) 2915 292 293### 高频接口中不要打印Trace和日志 294#### 类型 295响应时延/完成时延 296#### 解决方法 297排查接口onTouch、onItemDragMove、onDragMove、onDidScroll、onMouse、onVisibleAreaChange、OnAreaChange、 298onActionUpdate、animator的onFrame、组件复用场景下的aboutToReuse,不建议在里面打印trace和日志。 299#### 反例 300```typescript 301import { hiTraceMeter } from '@kit.PerformanceAnalysisKit'; 302 303@Component 304struct CounterOfOnDidScroll { 305 private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 306 307 build() { 308 Scroll() { 309 ForEach(this.arr, (item: number) => { 310 Text("ListItem" + item) 311 .width("100%") 312 .height("100%") 313 }, (item: number) => item.toString()) 314 } 315 .width('100%') 316 .height('100%') 317 .onDidScroll(() => { 318 hiTraceMeter.startTrace("ScrollSlide", 1002); 319 // 业务逻辑 320 // ... 321 // 在高频接口中不建议打印Trace和日志 322 hiTraceMeter.finishTrace("ScrollSlide", 1002); 323 }) 324 } 325``` 326#### 正例 327```typescript 328@Component 329struct PositiveOfOnDidScroll { 330 private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 331 332 build() { 333 Scroll() { 334 List() { 335 ForEach(this.arr, (item: number) => { 336 ListItem() { 337 Text("TextItem" + item) 338 }.width("100%") 339 .height(100) 340 }, (item: number) => item.toString()) 341 } 342 .divider({ strokeWidth: 3, color: Color.Gray }) 343 } 344 .width('100%') 345 .height('100%') 346 .onDidScroll(() => { 347 // 业务逻辑 348 // ... 349 }) 350 } 351} 352``` 353#### 高频程度&收益(5满分) 3544 355 356### 组件复用里面有if语句,但是未使用reuseId 357#### 类型 358完成时延/帧率 359#### 解决方法 360排查使用了@Reusable的自定义组件,查看build中给是否使用了if/else或ForEach等条件渲染语句,如果使用了,需要配合reuseId一起使用。 361#### 反例 362```typescript 363@Component 364@Reusable 365export struct MockComplexSubBranch { 366 @State alignStyle: FlexAlign = FlexAlign.Center; 367 368 aboutToReuse(params: Record<string, number>): void { // 缓存复用组件,更新组件的状态变量 369 this.alignStyle = params.alignStyle; 370 } 371 372 build() { 373 Column() { 374 Column({ space: 5 }) { 375 Text('ComplexSubBranch not reusable') 376 .fontSize($r('app.integer.font_size_9')) 377 .fontColor($r('app.color.hint_txt_color')) 378 .width($r('app.string.layout_90_percent')) 379 } 380 } 381 } 382} 383 384import { MockComplexSubBranch } from './MockComplexSubBranch'; 385 386@Component 387export struct WithoutReuseId { 388 @State isAlignStyleStart: boolean = true; 389 390 build() { 391 Column() { 392 Button("Change FlexAlign") 393 .onClick(() => { 394 this.isAlignStyleStart = !this.isAlignStyleStart; 395 }) 396 Stack() { 397 if (this.isAlignStyleStart) { 398 MockComplexSubBranch({ alignStyle: FlexAlign.Start }); // 未使用reuseId 399 } else { 400 MockComplexSubBranch({ alignStyle: FlexAlign.End }); 401 } 402 } 403 } 404 } 405} 406``` 407#### 正例 408```typescript 409@Component 410@Reusable 411// 添加Reusable装饰器,声明组件具备可复用的能力 412export struct MockComplexSubBranch { 413 @State alignStyle: FlexAlign = FlexAlign.Center; 414 415 aboutToReuse(params: Record<string, number>): void { 416 this.alignStyle = params.alignStyle; 417 } 418 419 build() { 420 Column() { 421 Column({ space: 5 }) { 422 Text('ComplexSubBranch reusable') 423 .fontSize($r('app.integer.font_size_9')) 424 .fontColor($r('app.color.hint_txt_color')) 425 .width($r('app.string.layout_90_percent')) 426 } 427 } 428 } 429} 430 431import { MockComplexReusableSubBranch } from './MockComplexReusableSubBranch'; 432 433@Component 434export struct WithReuseId { 435 @State isAlignStyleStart: boolean = true; 436 437 build() { 438 Column() { 439 Button("Change FlexAlign") 440 .onClick(() => { 441 this.isAlignStyleStart = !this.isAlignStyleStart; 442 }) 443 Stack() { 444 if (this.isAlignStyleStart) { 445 MockComplexSubBranch({ alignStyle: FlexAlign.Start }).reuseId("MockComplexSubBranchStart"); // 使用reuseId标识 446 } else { 447 MockComplexSubBranch({ alignStyle: FlexAlign.End }).reuseId("MockComplexSubBranchEnd"); 448 } 449 } 450 } 451 } 452} 453``` 454 455#### 高频程度&收益(5满分) 4564 457 458### 不建议使用@Prop装饰器 459#### 类型 460响应时延/完成时延 461#### 解决方法 462全局搜索@Prop并且替换。 463#### 反例 464```typescript 465@Observed 466class Book { 467 public c: number = 0; 468 469 constructor(c: number) { 470 this.c = c; 471 } 472} 473 474@Component 475struct PropChild { 476 @Prop testNum: Book; // @Prop装饰状态变量会深拷贝 477 478 build() { 479 Text(`PropChild testNum ${this.testNum.c}`) 480 } 481} 482 483@Entry 484@Component 485struct Parent1 { 486 @State testNum: Book[] = [new Book(1)]; 487 488 build() { 489 Column() { 490 Text(`Parent testNum ${this.testNum[0].c}`) 491 .onClick(() => { 492 this.testNum[0].c += 1; 493 }) 494 // PropChild没有改变@Prop testNum: Book的值,所以这时最优的选择是使用@ObjectLink 495 PropChild({ testNum: this.testNum[0] }) 496 } 497 } 498} 499``` 500#### 正例 501```typescript 502@Observed 503class Book { 504 public c: number = 0; 505 506 constructor(c: number) { 507 this.c = c; 508 } 509} 510 511@Component 512struct PropChild { 513 @ObjectLink testNum: Book; // @ObjectLink装饰状态变量不会深拷贝 514 515 build() { 516 Text(`PropChild testNum ${this.testNum.c}`) 517 } 518} 519 520@Entry 521@Component 522struct Parent2 { 523 @State testNum: Book[] = [new Book(1)]; 524 525 build() { 526 Column() { 527 Text(`Parent testNum ${this.testNum[0].c}`) 528 .onClick(() => { 529 this.testNum[0].c += 1; 530 }) 531 // 当子组件不需要发生本地改变时,优先使用 @ObjectLink,因为@Prop是会深拷贝数据,具有拷贝的性能开销,所以这个时候@ObjectLink是比@Link和 @Prop更优的选择 532 PropChild({ testNum: this.testNum[0] }) 533 } 534 } 535} 536``` 537#### 高频程度&收益(5满分) 5384 539 540### 避免在ResourceManager的getXXXSync接口入参中直接使用资源信息 541#### 类型 542响应时延/完成时延 543#### 解决方法 544排查ResourceManager.getXXXSync接口,查看入参时需要使用getStringSync($r('app.media.icon').id)的形式, 545如果未使用需要整改。 546 547#### 反例 548```typescript 549this.context.resourceManager.getStringSync($r('app.string.test')); 550``` 551#### 正例 552```typescript 553this.context.resourceManager.getStringSync($r('app.string.test').id); 554``` 555#### 高频程度&收益(5满分) 5563 557 558### 展示用的自定义组件(数据从父组件中获取,无独立数据处理)使用@Builder替换 559#### 类型 560响应时延/完成时延 561#### 解决方法 562审视@Component标记的自定义组件,如果里面没有独立的生命周期处理逻辑,数据由父组件传递,建议@Builder替代。 563#### 反例 564```typescript 565@Entry 566@Component 567struct CEMineButtomView { 568 build() { 569 View(); 570 } 571} 572 573@Component 574export struct ViewA { 575 build() { 576 Row() { 577 Text('- 到底了 -') 578 .fontSize(12) 579 .fontColor($r("app.color.color_1")) 580 }.justifyContent(FlexAlign.Center) 581 .width('100%') 582 .height(51) 583 .padding({ bottom: 21 }) 584 } 585} 586``` 587#### 正例 588```typescript 589@Builder 590function viewB() { 591 Row() { 592 Text('- 到底了 -').fontSize(12) 593 .fontColor($r("app.color.color_1")) 594 } 595 .justifyContent(FlexAlign.Center) 596 .width('100%') 597 .height(51) 598 .padding({ bottom: 21 }) 599} 600 601@Entry 602@Component 603struct CEMineButtomView { 604 build() { 605 Column(){ 606 viewB() 607 }.width('100%') 608 } 609} 610``` 611#### 高频程度&收益(5满分) 6123 613 614### 删除无具体逻辑的生命周期,ArkUI的函数回调等,删除冗余堵塞日志打印 615#### 类型 616响应时延/完成时延/帧率 617#### 解决方法 618排查所有的aboutToAppear、aboutToDisappear等生命周期函数,排查ArkUI的回调函数,如果函数中无具体业务逻辑, 619例如只打印了日志,删除函数回调。 620#### 反例 621```typescript 622import promptAction from '@ohos.promptAction'; 623 624@Entry 625@Component 626struct ViewA { 627 aboutToAppear(): void { 628 hilog.info('Index.ets aboutToAppear') // 无具体业务逻辑的日志 629 } 630 631 aboutToDisappear(): void{ 632 hilog.info('Index.ets aboutToDisappear') // 无具体业务逻辑的日志 633 } 634 635 /** 636 * 弹窗函数 637 */ 638 showToast() { 639 promptAction.showToast({ 640 message: $r('app.string.water_mark_toast_message') 641 }) 642 } 643 644 build() { 645 Column(){ 646 Text('测试一下') 647 .onClick(() => { 648 this.showToast(); // 有业务逻辑的方法 649 }) 650 }.width('100%') 651 } 652} 653``` 654#### 正例 655```typescript 656import promptAction from '@ohos.promptAction'; 657 658@Entry 659@Component 660struct ViewB { 661 /** 662 * 弹窗函数 663 */ 664 showToast() { 665 promptAction.showToast({ 666 message: $r('app.string.water_mark_toast_message') 667 }) 668 } 669 670 build() { 671 Column(){ 672 Text('测试一下') 673 .onClick(() => { 674 this.showToast(); // 有业务逻辑的方法 675 }) 676 }.width('100%') 677 } 678} 679``` 680#### 高频程度&收益(5满分) 6813 682 683### 删除未关联组件的状态变量装饰器 684#### 类型 685响应时延/完成时延 686#### 解决方法 687排查全局的状态变量装饰器,如果变量未关联组件,删除装饰器。 688#### 反例 689```typescript 690@Component 691struct ComponentA { 692 @State message: string = 'Hello World'; 693 @State textColor: string | Color = '#007DFF'; 694 @State bgcolor: string | Color = '#ffffff'; // 变量bgcolor是没有关联组件的 695 @State selectColor: string | Color = '#007DFF'; // 变量selectColor是没有关联组件的 696 697 build() { 698 Column(){ 699 Text(this.message) 700 .fontSize(50) 701 .fontWeight(FontWeight.Bold) 702 .fontColor(this.textColor) 703 } 704 } 705} 706``` 707#### 正例 708```typescript 709@Component 710struct ComponentB { 711 @State message: string = 'Hello World'; 712 @State textColor: string | Color = '#007DFF'; 713 bgcolor: string | Color = '#ffffff'; // 变量bgcolor是有关联组件的 714 selectColor: string | Color = '#007DFF'; // 变量selectColor是有关联组件的 715 716 build() { 717 Column(){ 718 Text(this.message) 719 .fontSize(50) 720 .fontWeight(FontWeight.Bold) 721 .fontColor(this.selectColor) 722 .backgroundColor(this.bgcolor) 723 } 724 } 725} 726``` 727#### 高频程度&收益(5满分) 7282 729 730### crypto-js性能差 731#### 类型 732帧率 733#### 解决方法 734排查buffer.from关键字,加密建议使用原生的cryptoFramework,然后将buffer替换为base64helper,性能提升10倍以上, 735且数据量越大越明显。 736#### 反例 737```typescript 738new Uint8Array(buffer.from(str,'base64').buffer); 739``` 740#### 正例 741```typescript 742let that = new util.Base64Helper(); 743let result = that.decodeSync(str); 744``` 745#### 高频程度&收益(5满分) 7462 747 748### 不建议使用Marquee组件 749#### 类型 750响应时延/完成时延 751#### 解决方法 752排查Marquee关键字,使用Text的跑马灯模式(TextOverflow.MARQUEE)替代。 753#### 反例 754```typescript 755struct ViewA { 756 build() { 757 Column() { 758 Marquee({ 759 start: this.start, 760 step: this.step, 761 loop: this.loop, 762 fromStart: this.fromStart, 763 src: this.src 764 }) 765 .width(360) 766 .height(80) 767 .fontColor('#FFFFFF') 768 .fontSize(48) 769 .fontWeight(700) 770 .backgroundColor('#182431') 771 .margin({ bottom: 40 }) 772 .onStart(() => { 773 console.info('Marquee animation complete onStart') 774 }) 775 .onBounce(() => { 776 console.info('Marquee animation complete onBounce') 777 }) 778 .onFinish(() => { 779 console.info('Marquee animation complete onFinish') 780 }) 781 }.width("100%") 782 } 783} 784``` 785#### 正例 786```typescript 787struct ViewB { 788 build(){ 789 Column(){ 790 Text(reply.user) 791 .maxLines(1) 792 .textOverflow({ overflow: TextOverflow.MARQUEE }) // 跑马灯模式 793 .width("30%") 794 }.width("100%") 795 } 796} 797``` 798#### 高频程度&收益(5满分) 7991 800 801### 不能使用函数作为ArkUI组件的属性和组件复用的自定义组件的入参 802#### 类型 803完成时延 804#### 解决方法 805查看属性是否有xx()函数写法,确认函数/方法中是否有耗时操作,替换成变量。 806#### 反例 807```typescript 808struct ViewA { 809 build() { 810 Column() { 811 List() { 812 LazyForEach(this.data, (item: string) => { 813 ListItem() { 814 // 此处sum参数是函数获取的,每次组件复用都会重复触发此函数的调用 815 ChildComponent({ desc: item, sum: this.count() }) 816 }.width('100%').height(100) 817 }, (item: string) => item) 818 } 819 } 820 } 821} 822``` 823#### 正例 824```typescript 825struct ViewB { 826 @State sum: number = 0; 827 828 aboutToAppear(): void { 829 this.sum = this.count(); 830 } 831 832 build() { 833 Column() { 834 List() { 835 LazyForEach(this.data, (item: string) => { 836 ListItem() { 837 ChildComponent({ desc: item, sum: this.sum }) 838 }.width('100%').height(100) 839 }, (item: string) => item) 840 } 841 } 842 } 843} 844 845``` 846#### 高频程度&收益(5满分) 8471 848 849### 不建议使用.linearGradient颜色渐变属性 850#### 类型 851完成时延 852#### 解决方法 853排查linearGradient关键字,可以使用图片代替。 854#### 反例 855```typescript 856Row() 857 .linearGradient({ 858 angle: 90, 859 colors: [[0xff0000, 0.0], [0x0000ff, 0.3], [0xffff00, 1.0]] 860 }) 861``` 862#### 正例 863```typescript 864Image($r('app.media.gradient_color')) 865``` 866#### 高频程度&收益(5满分) 8671 868 869### 不要在for/while循环中执行耗时操作 870#### 类型 871完成时延/帧率 872#### 解决方法 873排查for/while循环,查看里面是否有打印日志或者Trace。 874#### 反例 875```typescript 876@Component 877struct ViewA { 878 @State message: string = ""; 879 880 build() { 881 Column() { 882 Button('点击打印日志').onClick(() => { 883 for (let i = 0; i < 10; i++) { 884 console.info(this.message); 885 } 886 }) 887 } 888 } 889} 890``` 891#### 正例 892```typescript 893@Component 894struct ViewB { 895 @State message: string = ""; 896 897 build() { 898 Column() { 899 Button('点击打印日志').onClick(() => { 900 let logMessage: string = this.message; 901 for (let i = 0; i < 10; i++) { 902 console.info(logMessage); // 状态变量需先赋值,再调用会优化性能 903 } 904 }) 905 } 906 } 907} 908``` 909#### 高频程度&收益(5满分) 9101 911 912### 变量初值不建议设置为undefined,需进行默认初始化 913#### 类型 914完成时延 915#### 解决方法 916例如number设置为0,string设置为空字符串等,这样在使用过程中更不需要增加额外判空。 917排查类中的变量,看看是否有初始化为undefined。 918#### 反例 919```typescript 920@State channels?: Channels[] = undefined; 921``` 922#### 正例 923```typescript 924@State channels?: Channels[] = []; 925``` 926#### 高频程度&收益(5满分) 9271 928 929<!--no_check--> 930 931 932