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