• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 应用性能优化常见问题解决指导
2
3## 概述
4
5本文总结了实际开发应用时常见的性能优化规范,配合举例实际开发中常见的正反例代码,帮助开发者解决大部分性能问题。
6
7### 性能规范总览目录
8| &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;  &emsp;&emsp;  &emsp;&emsp; <br />分类<br />&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp; &emsp;&emsp;&emsp;&emsp;  &emsp;&emsp;   |<br />高频程度 (5满分)<br />&emsp;&emsp;&emsp;&emsp;   | 规范(检查项)                                             | 实操方法                |            <br />代码示例<br />&emsp;&emsp;&emsp;&emsp;                 |
9|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------:|:----------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------:|
10| 响应时延&nbsp;/&nbsp;完成时延                                                                                                                                                    |                          5                          | 不建议在aboutToAppear(),aboutToDisappear()等生命周期中执行耗时操作。 | 排查所有的aboutToAppear和aboutToDisappear函数(或者通过Trace查看),查看是否有耗时操作,改为setTimeOut或者在TaskPool中执行。                                          |       [代码示例](#不建议在abouttoappearabouttodisappear等生命周期中执行耗时操作)        |
11| 响应时延&nbsp;/&nbsp;完成时延                                                                                                                                                    |                          5                          | 不要在回调函数中执行耗时操作(ArkUI接口回调、网络访问回调、await等)。            | 排查所有的回调函数(或者通过Trace查看),尤其是ArkUI接口,网络回调函数,查看是否有耗时操作,是否使用了await操作,改为setTimeOut或者在TaskPool中执行。                      |            [代码示例](#不要在回调函数中执行耗时操作arkui接口回调网络访问回调await等)             |
12| 响应时延&nbsp;/&nbsp;完成时延&nbsp;/&nbsp;帧率                                                                                                                                     |                          5                          | 列表场景未使用LazyForEach+组件复用+缓存列表项。                      | 排查使用LazyForEach的代码,确认是否有使用组件复用(@Reusable)+缓存列表项(cachedCount)。                                                                                                           |                [代码示例](#列表场景未使用lazyforeach组件复用缓存列表项)                 |
13| 完成时延                                                                                                                                                                     |                          5                          | Web未使用预连接,未提前初始化引擎。                                 | 在应用创建Ability的时候,在OnCreate阶段预先初始化内核,建议把引擎的初始化放在setTimeOut中。                                                                                                              |                     [代码示例](#web未使用预连接未提前初始化引擎)                      |
14| 响应时延&nbsp;/&nbsp;完成时延                                                                                                                                                    |                          5                          | 高频接口中不要打印Trace和日志。                                  | 排查接口onTouch、onItemDragMove、onDragMove、onDidScroll、onMouse、onVisibleAreaChange、OnAreaChange、onActionUpdate、animator的onFrame、组件复用场景下的aboutToReuse,不建议在里面打印trace和日志。          |                     [代码示例](#高频接口中不要打印trace和日志)                      |
15| 完成时延&nbsp;/&nbsp;帧率                                                                                                                                                      |                          4                          | 组件复用里面有if语句,但是未使用reuseId。                           | 排查使用了@Reusable的自定义组件,查看build中给是否使用了if/else或ForEach等条件渲染语句,如果使用了,需要配合reuseId一起使用。                                                                                        |                  [代码示例](#组件复用里面有if语句但是未使用reuseid)                   |
16| 响应时延&nbsp;/&nbsp;完成时延                                                                                                                                                    |                          4                          | 不建议使用@Prop装饰器。                                      | 全局搜索@Prop并且替换                                                                                                                                           |                        [代码示例](#不建议使用prop装饰器)                        |
17| 响应时延&nbsp;/&nbsp;完成时延                                                                                                                                                    |                          3                          | 避免在ResourceManager的getXXXSync接口入参中直接使用资源信息。         | 排查ResourceManager.getXXXSync接口,查看入参时需要使用getStringSync($r('app.media.icon').id)的形式,如果未使用需要整改。                                                 | [代码示例](#避免在resourcemanager的getxxxsync接口入参中直接使用资源信息) |
18| 响应时延&nbsp;/&nbsp;完成时延                                                                                                                                                    |                          3                          | 展示用的自定义组件(数据从父组件中获取,无独立数据处理)使用@Builder替换。           | 审视@Component标记的自定义组件,如果里面没有独立的生命周期处理逻辑,数据由父组件传递,建议@Builder替代。                                                           |            [代码示例](#展示用的自定义组件数据从父组件中获取无独立数据处理使用builder替换)            |
19| 响应时延&nbsp;/&nbsp;完成时延&nbsp;/&nbsp;帧率                                                                                                                                     |                          3                          | 删除无具体逻辑的生命周期,ArkUI的函数回调等,删除冗余堵塞日志打印。                | 排查所有的aboutToAppear、aboutToDisappear等生命周期函数,排查ArkUI的回调函数,如果函数中无具体业务逻辑,例如只打印了日志,删除函数回调。                                        |             [代码示例](#删除无具体逻辑的生命周期arkui的函数回调等删除冗余堵塞日志打印)              |
20| 响应时延&nbsp;/&nbsp;完成时延                                                                                                                                                    |                          3                          | 删除未关联组件的状态变量装饰器。                                    | 排查全局的状态变量装饰器,如果变量未关联组件,删除装饰器。             |                      [代码示例](#删除未关联组件的状态变量装饰器)                       |
21| 帧率                                                                                                                                                                       |                          2                          | 	crypto-js性能差。                                      | 排查buffer.from关键字,加密建议使用原生的cryptoFramework,然后将buffer替换为base64helper,性能提升10倍以上, 且数据量越大越明显。             |                        [代码示例](#crypto-js性能差)                        |
22| 响应时延&nbsp;/&nbsp;完成时延                                                                                                                                                    |                          1                          | 	不建议使用Marquee组件。                                    | 排查Marquee关键字,使用Text的跑马灯模式(TextOverflow.MARQUEE)替代。             |                       [代码示例](#不建议使用marquee组件)                       |
23| 完成时延                                                                                                                                                                     |                          1                          | 	不能使用函数作为ArkUI组件的属性和组件复用的自定义组件的入参。                  | 查看属性是否有xx()函数写法,确认函数/方法中是否有耗时操作,替换成变量。            |              [代码示例](#不能使用函数作为arkui组件的属性和组件复用的自定义组件的入参)              |
24| 完成时延                                                                                                                                                                     |                          1                          | 	不建议使用.linearGradient颜色渐变属性。                        | 排查linearGradient关键字,可以使用图片代替。            |                 [代码示例](#不建议使用lineargradient颜色渐变属性)                  |
25| 完成时延&nbsp;/&nbsp;帧率                                                                                                                                                      |                          1                          | 	不要在for/while循环中执行耗时操作。                             | 排查for/while循环,查看里面是否有打印日志或者Trace。            |                    [代码示例](#不要在forwhile循环中执行耗时操作)                    |
26| 完成时延&nbsp;/&nbsp;帧率                                                                                                                                                      |                          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