• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 应用开发性能优化入门引导
2
3<!--Kit: Common-->
4<!--Subsystem: Demo&Sample-->
5<!--Owner: @mgy917-->
6<!--Designer: @jiangwensai-->
7<!--Tester: @Lyuxin-->
8<!--Adviser: @huipeizi-->
9
10## 概述
11
12在开发应用时,优化应用性能是至关重要的。本文将介绍应用开发过程中常见的一些性能问题,并提供相应的解决方案,配合相关参考示例,帮助开发者解决大部分性能问题。
13
14应用性能分析的方法划分为了**性能分析四要素**,下面将介绍如何使用性能分析四要素,解决应用开发过程中的性能问题。
15
16* **第一要素:合理使用并行化、预加载和缓存**,需要合理地使用并行化、预加载和缓存等方法,例如使用多线程并发、异步并发、Web预加载等能力,提升系统资源利用率,减少主线程负载,加快应用的启动速度和响应速度。
17
18* **第二要素:尽量减少布局的嵌套层数**,在进行页面布局开发时,应该去除冗余的布局嵌套,使用相对布局、绝对定位、自定义布局、Grid、GridRow等扁平化布局,减少布局的嵌套层数,避免系统绘制更多的布局组件,达到优化性能、减少内存占用的目的。
19
20* **第三要素:合理管理状态变量**,应该合理地使用状态变量,精准控制组件的更新范围,控制状态变量关联组件数量,控制对象级状态变量的成员变量关联组件数,减少系统的组件渲染负载,提升应用流畅度。
21
22* **第四要素:合理使用系统接口,避免冗余操作**,应该合理使用系统的高频回调接口,删除不必要的Trace和日志打印,避免注册系统冗余回调,减少系统开销。
23
24## 第一要素:合理使用并行化、预加载和缓存
25
26需要合理地使用并行化、预加载和缓存等方法,提升系统资源利用率,减少主线程负载,加快应用的启动速度和响应速度。
27
28### 使用并行化提升启动速度
29
30自定义组件创建完成之后,在build函数执行之前,将先执行[aboutToAppear](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#abouttoappear)生命周期回调函数。此时若在该函数中执行耗时操作,将阻塞UI渲染,增加UI主线程负担。因此,应尽量避免在自定义组件的生命周期内执行高耗时操作。在aboutToAppear生命周期函数内建议只做当前组件的初始化逻辑,对于不需要等待结果的高耗时任务,可以使用多线程处理该任务,通过并发的方式避免主线程阻塞;也可以把耗时操作改为异步并发或延后处理,保证主线程优先处理组件绘制逻辑。
31
32**使用多线程执行耗时操作**
33
34在日常开发过程中经常会碰到这样的问题:主页的开发场景中有多个Tab页展示不同内容,在首次加载完主页后,切换到第二个Tab页时需要加载和处理网络数据,导致第二个Tab页的页面显示较慢,有较大的完成时延。
35
36碰到此类问题,可以在生命周期aboutToAppear中,使用多线程并发、[高效并发编程](efficient-concurrent-programming.md)、[多线程能力场景化示例实践](multi_thread_capability.md)的方法执行第二个Tab页的网络数据访问解析、数据加载等耗时操作,既可以提前完成数据加载,也不会影响主线程UI绘制和渲染。
37
38使用TaskPool进行耗时操作的示例代码如下:
39
40```typescript
41import { IconItemSource } from '../utils/IconItemSource';
42import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
43import { loadPicture } from '../utils/IndependentTask';
44import { taskpool } from '@kit.ArkTS';
45
46@Component
47export struct PageOnePositive {
48  @State private text: string = "";
49  pathStack: NavPathStack = new NavPathStack();
50  @State fontColor: string = '#182431';
51  @State selectedFontColor: string = '#007DFF';
52  @State currentIndex: number = 0;
53  @State selectedIndex: number = 0;
54  private controller: TabsController = new TabsController();
55
56  loadPicture(count: number): IconItemSource[] {
57    let iconItemSourceList: IconItemSource[] = [];
58    // 遍历添加6*count个IconItem的数据
59    for (let index = 0; index < count; index++) {
60      const numStart: number = index * 6;
61      // 此处循环使用6张图片资源
62      iconItemSourceList.push(new IconItemSource($r('app.media.bigphoto'), `item${numStart + 1}`));
63      iconItemSourceList.push(new IconItemSource($r('app.media.bigphoto'), `item${numStart + 2}`));
64      iconItemSourceList.push(new IconItemSource($r('app.media.bigphoto'), `item${numStart + 3}`));
65      iconItemSourceList.push(new IconItemSource($r('app.media.bigphoto'), `item${numStart + 4}`));
66      iconItemSourceList.push(new IconItemSource($r('app.media.bigphoto'), `item${numStart + 5}`));
67      iconItemSourceList.push(new IconItemSource($r('app.media.bigphoto'), `item${numStart + 6}`));
68    }
69    return iconItemSourceList;
70  }
71
72  requestByTaskPool(): void {
73    hiTraceMeter.startTrace("responseTime", 1002);
74    // 耗时任务,TaskPool执行
75    let iconItemSourceList: IconItemSource[] = [];
76    // 创建Task
77    let lodePictureTask: taskpool.Task = new taskpool.Task(loadPicture, 100000);
78    // 执行Task,并返回结果
79    taskpool.execute(lodePictureTask).then((res: object) => {
80      iconItemSourceList = res as IconItemSource[];
81      iconItemSourceList = [];
82      // loadPicture方法的执行结果
83    })
84    hiTraceMeter.finishTrace("responseTime", 1002);
85  }
86
87  build() {
88    // ...
89      Column() {
90
91        Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
92          // TabContent
93          // ...
94        }
95        // ...
96        .onContentWillChange((currentIndex, comingIndex) => {
97          if (comingIndex == 1) {
98            this.requestByTaskPool();
99            let context: Context = this.getUIContext().getHostContext() as Context;
100            this.text = context.resourceManager.getStringSync($r('app.string.startup_text2').id);
101          }
102          return true
103        })
104        // ...
105      }
106  }
107}
108```
109
110其他多线程并发相关文章:
111
112* [利用native的方式实现跨线程调用](native-threads-call-js.md)
113
114**使用异步执行耗时操作**
115
116问题:在aboutToAppear生命周期函数中,运行了业务数据解析和处理等耗时操作,影响了上一页面点击跳转该页面的响应时延。
117
118可以把耗时操作的执行从同步执行改为异步或者延后执行,[提升应用冷启动速度](improve-application-cold-start-speed.md),比如使用setTimeOut执行耗时操作,示例如下:
119
120```typescript
121import { hilog, hiTraceMeter } from '@kit.PerformanceAnalysisKit';
122
123const DELAYED_TIME: number = 100;
124const LARGE_NUMBER: number = 200000;
125
126@Component
127export struct PageTwoPositive {
128  @State message: string = 'Hello World';
129  @State private text: string = "";
130  pathStack: NavPathStack = new NavPathStack();
131  private count: number = 0;
132
133  aboutToAppear(): void {
134    // 在aboutToAppear接口中对耗时间的计算任务进行了异步处理。
135    // 耗时操作
136    this.computeTaskAsync(); // 异步任务
137    let context: Context = this.getUIContext().getHostContext() as Context;
138    this.text = context.resourceManager.getStringSync($r('app.string.startup_text4').id);
139  }
140
141  computeTask(): void {
142    hiTraceMeter.startTrace("responseTime", 1002);
143    this.count = 0;
144    while (this.count < LARGE_NUMBER) {
145      this.count++;
146      hilog.info(0x0000, 'count', '%{public}s', JSON.stringify(this.count));
147    }
148    hiTraceMeter.finishTrace("responseTime", 1002);
149  }
150
151  // 运算任务异步处理
152  private computeTaskAsync(): void {
153    setTimeout(() => {
154      // 这里使用setTimeout来实现异步延迟运行
155      this.computeTask();
156    }, DELAYED_TIME)
157  }
158
159  build() {
160    // 页面布局
161    // ...
162  }
163}
164```
165
166### 使用预加载提升页面启动和响应速度
167
168应该合理使用系统的预加载能力,例如Web组件的预连接、预加载、预渲染,使用List、Swiper、Grid、WaterFlow等组件的cachedCount属性实现预加载,使用条件渲染实现预加载)等,提升页面的启动和响应速度。
169
170**使用Web组件的预连接、预加载、预渲染能力**
171
172当遇到Web页面加载慢的场景,可以使用Web组件的预连接、预加载、预渲染能力,使用[Web组件开发性能提升指导](performance-web-import.md),在应用空闲时间提前进行Web引擎初始化和页面加载,提升下一页面的启动和响应速度。
173
174示例代码如下:
175
176```typescript
177import webview from '@ohos.web.webview';
178
179preload() {
180  // Web组件引擎初始化
181  webview.WebviewController.initializeWebEngine();
182  // 启动预连接,连接地址为即将打开的网址
183  webview.WebviewController.prepareForPageLoad('https://www.example.com', true, 2);
184}
185```
186
187**使用cachedCount属性实现预加载**
188
189推荐在使用List、Swiper、Grid、WaterFlow等组件时,配合使用cachedCount属性实现预加载,详情指导在[WaterFlow高性能开发指导](waterflow_optimization.md)、[Swiper高性能开发指导](swiper_optimization.md)、[Grid高性能开发指导](grid_optimization.md)、[列表场景性能提升实践](list-perf-improvment.md),示例代码如下所示:
190
191```typescript
192  private source: MyDataSource = new MyDataSource();
193
194  build() {
195    List() {
196      LazyForEach(this.source, item => {
197        ListItem() {
198          Text("Hello" + item)
199            .fontSize(50)
200            .onAppear(() => {
201              console.info("appear:" + item);
202            })
203        }
204      })
205    }.cachedCount(3) // 扩大数值appear日志范围会变大
206  }
207```
208
209**使用条件渲染实现预加载**
210
211问题:页面布局复杂度较高,导致跳转该页面的响应时延较高。
212
213可以使用条件渲染的方式进行[合理选择条件渲染和显隐控制](proper-choice-between-if-and-visibility.md),添加页面的简单骨架图作为默认展示页面,等数据加载完成后再显示最终的复杂布局,加快点击响应速度。
214
215示例代码如下:
216
217```typescript
218import skeletonComponent from "./skeletonComponent";
219import businessComponent from "./businessComponent";
220
221@State isInitialized: boolean = false;
222
223build() {
224  // 当数据未就位时展示骨架图,提升点击响应速度,减少页面渲染时间
225  if(!this.isInitialized) {
226    // 网络数据未获取前使用骨架图
227    skeletonComponent();
228  } else {
229    // 数据获取后再刷新显示内容
230    businessComponent();
231  }
232}
233```
234
235### 使用缓存提升启动速度和滑动帧率
236
237在列表场景中,推荐使用LazyForEach+组件复用+缓存列表项的能力,替代Scroll/ForEach实现滚动列表场景的实现,加快页面启动速度,提升滑动帧率;在一些属性动画的场景下,可以使用renderGroup缓存提升属性动画性能;也可以使用显隐控制对页面进行缓存,加快页面的显示响应速度。
238
239**组件复用**
240
241应用框架提供了组件复用能力,可复用组件从组件树上移除时,会进入到一个回收缓存区。后续创建新组件节点时,会复用缓存区中的节点,节约组件重新创建的时间。
242
243若业务实现中存在以下场景,并成为UI线程的帧率瓶颈,推荐使用组件复用,具体指导在[组件复用实践](component-recycle.md)、[列表场景性能提升实践](list-perf-improvment.md)、[组件复用总览](component-reuse-overview.md):
244
245* 列表滚动(本例中的场景):当应用需要展示大量数据的列表,并且用户进行滚动操作时,频繁创建和销毁列表项的视图可能导致卡顿和性能问题。在这种情况下,使用列表组件的组件复用机制可以重用已经创建的列表项视图,提高滚动的流畅度。
246* 动态布局更新:如果应用中的界面需要频繁地进行布局更新,例如根据用户的操作或数据变化动态改变视图结构和样式,重复创建和销毁视图可能导致频繁的布局计算,影响帧率。在这种情况下,使用组件复用可以避免不必要的视图创建和布局计算,提高性能。
247* 地图渲染:在地图渲染这种场景下,频繁创建和销毁数据项的视图可能导致性能问题。使用组件复用可以重用已创建的视图,只更新数据的内容,减少视图的创建和销毁,能有效提高性能。
248
249示例代码如下:
250
251```typescript
252// xxx.ets
253class MyDataSource implements IDataSource {
254  private dataArray: string[] = [];
255  private listener: DataChangeListener | undefined;
256  public pushData(item:string){
257    this.dataArray.push(item);
258  }
259  // ...
260}
261
262@Entry
263@Component
264struct MyComponent {
265  private data: MyDataSource = new MyDataSource();
266
267  aboutToAppear() {
268    for (let i = 0; i < 1000; i++) {
269      this.data.pushData(i.toString());
270    }
271  }
272
273  build() {
274    List({ space: 3 }) {
275      LazyForEach(this.data, (item: string) => {
276        ListItem() {
277          ReusableChildComponent({ item: item })
278        }
279      }, (item: string) => item)
280    }
281    .width('100%')
282    .height('100%')
283  }
284}
285
286@Reusable
287@Component
288struct ReusableChildComponent {
289  @State item: string = '';
290  // 复用时触发的生命周期
291  aboutToReuse(params: ESObject) {
292    this.item = params.item;
293  }
294
295  build() {
296    Row() {
297      Text(this.item)
298        .fontSize(20)
299        .margin({ left: 10 })
300    }.margin({ left: 10, right: 10 })
301  }
302}
303```
304
305**使用renderGroup缓存提升属性动画性能**
306
307页面响应时,可能大量使用属性动画和转场动画,当复杂度达到一定程度之后,就有可能出现卡顿的情况。[renderGroup](reasonable-using-renderGroup.md)是组件通用方法,它代表了渲染绘制的一个组合。
308
309具体原理是在首次绘制组件时,若组件被标记为启用renderGroup状态,将对组件及其子组件进行离屏绘制,将绘制结果合并保存到缓存中。此后当需要重新绘制相同组件时,就会优先使用缓存而不必重新绘制了,从而降低绘制负载,进而加快响应速度。
310
311示例代码如下:
312
313```typescript
314// Index.ets
315import { IconItem } from './IconItem';
316
317// IconItem相关数据
318class IconItemSource {
319  image: string | Resource = ''
320  text: string | Resource = ''
321
322  constructor(image: string | Resource = '', text: string | Resource = '') {
323    this.image = image;
324    this.text = text;
325  }
326}
327
328@Entry
329@Component
330struct Index {
331  private iconItemSourceList: IconItemSource[] = [];
332
333  aboutToAppear() {
334    // 遍历添加IconItem的数据
335    this.iconItemSourceList.push(
336      new IconItemSource($r('app.media.img1'), `label1`),
337      new IconItemSource($r('app.media.img2'), `label2`),
338      new IconItemSource($r('app.media.img3'), `label3`)
339    );
340  }
341
342  build() {
343    Column() {
344      // IconItem放置在grid内
345      GridRow() {
346        ForEach(this.iconItemSourceList, (item: IconItemSource) => {
347          GridCol() {
348            IconItem({ image: item.image, text: item.text })
349              .transition(TransitionEffect.scale({ x: 0, y: 0 })
350                  .animation({ delay: 1000, duration: 1000 })
351                  .combine(TransitionEffect.rotate({ z: 1, angle: 180 })
352                  .animation({ duration: 1000 }))
353              )
354          }
355        })
356      }
357    }
358  }
359}
360
361// IconItem.ets
362@Component
363export struct IconItem {
364  renderGroupFlag: boolean = false;
365  image: string | Resource = '';
366  text: string | Resource = '';
367
368  build() {
369    Flex({
370      direction: FlexDirection.Column,
371      justifyContent: FlexAlign.Center,
372      alignContent: FlexAlign.Center
373    }) {
374      Image(this.image)
375      Text(this.text)
376      // ...
377    }
378    // 在IconItem内开启renderGroup
379    .renderGroup(true)
380  }
381}
382```
383
384**使用显隐控制进行页面缓存**
385
386控制元素显示与隐藏是一种常见的场景,使用Visibility.None、if条件判断等都能够实现该效果。其中if条件判断控制的是组件的创建、布局阶段,Visibility属性控制的是元素在布局阶段是否参与布局渲染。使用时如果使用的方式不当,将引起性能上的问题。
387如果会频繁响应显示与隐藏的交互效果,建议使用切换Visibility.NoneVisibility.Visible来[合理控制元素显示与隐藏](proper-choice-between-if-and-visibility.md),在组件无需展示的时候进行缓存,提高性能。
388
389示例代码如下:
390
391```typescript
392@State isVisible: boolean = true;
393
394build() {
395  Column() {
396    Button("Switch visible and hidden").onClick(() => {
397        this.isVisible = !this.isVisible;
398    })
399    Stack() {
400      Scroll() {
401        Column() {
402          Image($r('app.media.icon'))
403        }
404      }.visibility(this.isVisible ? Visibility.Visible : Visibility.None)// 使用显隐控制切换,不会频繁创建与销毁组件
405    }
406  }
407}
408```
409
410## 第二要素:尽量减少布局的嵌套层数
411
412在进行页面布局开发时,应该去除冗余的布局嵌套,使用相对布局、绝对定位、自定义布局、Grid、GridRow等扁平化布局,减少布局的嵌套层数,避免系统绘制更多的布局组件,达到[优化布局性能](reduce-view-nesting-levels.md)、减少内存占用的目的。
413
414### 移除冗余节点
415
416应该删除冗余的布局嵌套,例如build最外层的无用容器嵌套、无用的Stack或Column嵌套等,减少布局层数。
417
418**删除无用的Stack/Column/Row嵌套**
419
420例如可能会在Row容器包含一个同样也是Row容器的子级。这种嵌套实际是多余的,并且会给布局层次结构造成不必要的开销。示例代码如下:
421
422```typescript
423// 反例
424Row() {
425  Row() {
426    Text()
427    Text()
428  }
429  Text()
430}
431
432// 正例
433Row() {
434  Text()
435  Text()
436  Text()
437}
438```
439
440**删除build函数中最外层无用容器嵌套**
441
442在开发过程中,布局的实现往往嵌套使用大量的自定义组件,build中冗余的最外层无用容器会大大增强嵌套层级,应该删除。
443
444反例代码如下:
445
446```typescript
447@Entry
448@Component
449struct ComponentA {
450  build() {
451    Column() {
452      ComponentB();
453    }
454  }
455}
456
457@Component
458struct ComponentB {
459  build() {
460    Column() {
461      Text('');
462    }
463  }
464}
465```
466
467正例代码如下:
468
469```typescript
470@Entry
471@Component
472struct ComponentA {
473  build() {
474    Column() {
475      ComponentB();
476    }
477  }
478}
479
480@Component
481struct ComponentB {
482  build() {
483    Text('');
484  }
485}
486```
487
488### 使用扁平化布局减少节点数
489
490**使用Column/Row替代Flex构建线性布局**
491
492由于Flex本身带来的二次布局的影响,Flex的性能明显低于Column和Row容器,因此推荐使用Column/Row替代Flex构建线性布局,具体指导在[Flex布局性能提升使用指导](flex-development-performance-boost.md)。
493
494反例代码如下:
495
496```typescript
497@Entry
498@Component
499struct MyComponent {
500  build() {
501    Flex({ direction: FlexDirection.Column }) {
502      Flex().width(300).height(200).backgroundColor(Color.Pink)
503      Flex().width(300).height(200).backgroundColor(Color.Yellow)
504      Flex().width(300).height(200).backgroundColor(Color.Grey)
505    }
506  }
507}
508```
509
510正例代码如下:
511
512```typescript
513@Entry
514@Component
515struct MyComponent {
516  build() {
517    Column() {
518      Row().width(300).height(200).backgroundColor(Color.Pink)
519      Row().width(300).height(200).backgroundColor(Color.Yellow)
520      Row().width(300).height(200).backgroundColor(Color.Grey)
521    }
522  }
523}
524```
525
526**使用Flex、List、Grid、RelativeContainer、绝对布局和自定义布局等构建复杂布局**
527
528复杂布局提供了场景化的能力,[优化布局性能](reduce-view-nesting-levels.md)可解决一种或者多种布局场景:
529
530* 使用Flex构建弹性布局;
531* List既具备线性布局的特点,同时支持懒加载和滑动的能力;
532* Grid/GridItem提供了宫格布局的能力,同时也支持懒加载和滑动能力;
533* RelativeContainer是一种相对布局,通过描述各个内容组件间相互关系来指导内容元素的布局过程,可从横纵两个方面进行布局描述,是一种二维布局算法。
534
535反例代码如下:
536
537```typescript
538@Entry
539@Component
540struct AspectRatioExample12 {
541  @State children: number[] = Array.from(Array<number>(900), (v, k) => k);
542
543  build() {
544    Scroll() {
545      Grid() {
546        ForEach(this.children, (item: number) => {
547          GridItem() {
548            Stack() {
549              Stack() {
550                Stack() {
551                  Text(item.toString())
552                }.size({ width: "100%"})
553              }.backgroundColor(Color.Yellow)
554            }.backgroundColor(Color.Pink)
555          }
556        }, (item: number) => item.toString())
557      }
558      .columnsTemplate('1fr 1fr 1fr 1fr')
559      .columnsGap(0)
560      .rowsGap(0)
561      .size({ width: "100%", height: "100%" })
562    }
563  }
564}
565```
566
567正例代码如下:
568
569```typescript
570@Entry
571@Component
572struct AspectRatioExample11 {
573  @State children: number[] = Array.from(Array<number>(900), (v, k) => k);
574
575  build() {
576    Scroll() {
577      Grid() {
578        ForEach(this.children, (item: number) => {
579          GridItem() {
580            Text(item.toString())
581          }.backgroundColor(Color.Yellow)
582        }, (item: number) => item.toString())
583      }
584      .columnsTemplate('1fr 1fr 1fr 1fr')
585      .columnsGap(0)
586      .rowsGap(0)
587      .size({ width: "100%", height: "100%" })
588    }
589  }
590}
591```
592
593## 第三要素:合理管理状态变量
594
595应该合理地使用状态变量,[精准控制组件的更新范围](precisely-control-render-scope.md),控制状态变量关联组件数量上限,控制对象级状态变量的成员变量关联组件数,减少系统的组件渲染负载,提升应用流畅度。
596
597### 精准控制组件的更新范围
598
599在复杂页面开发的场景下,精准控制组件更新的范围对提高应用运行性能尤为重要。应该避免状态变量的滥用引起的容器组件的刷新,进而影响帧率。
600
601**使用指定宽高的容器限制刷新范围**
602
603当在一个同时指定宽高的容器里改变容器内部的布局,那么只会在该容器内部做布局和测量更新,不会扩散影响到容器外面的组件。
604
605反例代码如下:
606
607```typescript
608struct StackExample {
609  @State isVisible: boolean = true;
610  private data: number[] = [];
611
612  aboutToAppear() {
613    for (let i: number = 0; i < Constants.IMAGE_TOTAL_NUM; i++) {
614      this.data.push(i);
615    }
616  }
617
618  build() {
619    Column() {
620      Button('Switch Hidden and Show').onClick(() => {
621        this.isVisible = !this.isVisible;
622      })
623
624      Stack() {
625        if (this.isVisible) {
626          Text('New Page').width(100).height(30).backgroundColor(0xd2cab3)
627        }
628      }.width(100) // 本案例以Stack容器为例,只指定了宽,会触发父容器组件重新布局计算,引起ForEach中文本测量。
629
630      ForEach(this.data, (item: number) => { // 由于Stack容器没有同时指定宽高,会扩散影响到这一层,引起Text的测量更新。
631        Text(`Item value: ${item}`)
632          .fontSize($r('app.integer.font_size_20'))
633          .width($r('app.string.layout_100_percent'))
634          .textAlign(TextAlign.Center)
635      }, (item: number) => item.toString())
636    }
637  }
638}
639```
640
641正例代码如下:
642
643```typescript
644struct StackExample2 {
645  @State isVisible: boolean = true;
646  private data: number[] = [];
647
648  aboutToAppear() {
649    for (let i: number = 0; i < Constants.IMAGE_TOTAL_NUM; i++) {
650      this.data.push(i);
651    }
652  }
653
654  build() {
655    Column() { // 父容器
656      Button('Switch Hidden and Show').onClick(() => {
657        this.isVisible = !this.isVisible;
658      })
659
660      Stack() {
661        if (this.isVisible) {
662          Text('New Page').width(100).height(30).backgroundColor(0xd2cab3)
663        }
664      }.width(100).height(30) // 在指定宽高的Stack容器内,内部的Text组件变化只会在容器内部做布局和测量更新,不会影响到容器外ForEach中的Text组件。
665
666      ForEach(this.data, (item: number) => { // Stack容器指定了宽高,不会影响到这一层兄弟节点
667        Text(`Item value: ${item}`)
668          .fontSize($r('app.integer.font_size_20'))
669          .width($r('app.string.layout_100_percent'))
670          .textAlign(TextAlign.Center)
671      }, (item: number) => item.toString())
672    }
673  }
674```
675**减少不必要的参数层次传递**
676
677@State+@Prop、@State+@Link、@State+@Observed+@ObjectLink三种方案的实现方式是逐级向下传递状态,当共享状态的组件间层级相差较大时,会出现状态层层传递的现象。对于没有使用该状态的中间组件而言,这是“额外的消耗”。因此,对于跨越多层的状态变量传递,使用@Provide+@Consume方案更为合理。
678
679反例代码如下:
680
681```typescript
682interface Data {
683  text: string;
684}
685
686function getData(): Data {
687  return {
688    text: 'parent'
689  }
690}
691
692// 父组件
693@Component
694struct componentParent{
695  @State data: Data = {
696    text: ''
697  };
698
699  aboutToAppear() {
700    // 获取子组件数据
701    this.data = getData();
702  }
703
704  build() {
705    Column() {
706      componentSon({ data: this.data })
707    }
708  }
709}
710
711// 子组件
712@Component
713struct componentSon{
714  // 获取传递参数
715  @Prop data: Data;
716
717  build() {
718    Column() {
719      Text(this.data.text)
720      componentGrandSon({ data: this.data })
721    }
722  }
723}
724
725@Component
726struct componentGrandSon{
727  // 获取传递参数
728  @Prop data: Data;
729
730  build() {
731    Column() {
732      Text(this.data.text)
733    }
734  }
735}
736```
737
738正例代码如下:
739
740```typescript
741interface Data {
742  text: string;
743}
744
745function getData(): Data {
746  return {
747    text: 'parent'
748  }
749}
750
751// 父组件
752@Component
753struct componentParent{
754  @Provide('data') data: Data = {
755    text: ''
756  };
757
758  aboutToAppear() {
759    // 获取子组件数据
760    this.data = getData();
761  }
762
763  build() {
764    Column() {
765      componentSon()
766    }
767  }
768}
769
770// 子组件
771@Component
772struct componentSon{
773  // 获取传递参数
774  @Consume("data") data: Data;
775
776  build() {
777    Column() {
778      Text(this.data.text)
779      componentGrandSon()
780    }
781  }
782}
783
784@Component
785struct componentGrandSon{
786  // 获取传递参数
787  @Consume("data") data: Data;
788
789  build() {
790    Column() {
791      Text(this.data.text)
792    }
793  }
794}
795```
796
797**避免滥用@Provide+@Consume**
798
799在父子组件关联的场景下,@Provide+@Consume开销要大于@State+@Prop/@Link,因此在该场景下推荐使用@State+@Prop/@Link的组合。
800
801反例代码如下:
802
803```typescript
804interface Data {
805  text: string;
806}
807
808function getData(): Data {
809  return {
810    text: 'parent'
811  }
812}
813
814// 父组件
815@Component
816struct componentParent{
817  @Provide("data") data: Data = {
818    text: ''
819  };
820
821  aboutToAppear() {
822    // 获取子组件数据
823    this.data = getData();
824  }
825
826  build() {
827    Column() {
828      componentSon()
829    }
830  }
831}
832
833// 子组件
834@Component
835struct componentSon{
836  // 获取传递参数
837  @Consume("data") data: Data;
838
839  build() {
840    Column() {
841      Text(this.data.text)
842    }
843  }
844}
845```
846
847正例代码如下:
848
849```typescript
850interface Data {
851  text: string;
852}
853
854function getData(): Data {
855  return {
856    text: 'parent'
857  }
858}
859
860// 父组件
861@Component
862struct componentParent{
863  @State data:Data = {
864    text: ''
865  };
866
867  aboutToAppear() {
868    // 获取子组件数据
869    this.data = getData();
870  }
871
872  build() {
873    Column() {
874      componentSon({ data: this.data })
875    }
876  }
877}
878
879// 子组件
880@Component
881struct componentSon{
882  // 获取传递参数
883  @Prop data:Data;
884
885  build() {
886    Column() {
887      Text(this.data.text)
888    }
889  }
890}
891```
892
893### 精准控制状态变量关联组件数量
894
895应该控制状态变量关联的组件数量,如果一个状态关联过多的组件,当这个变量更新时会引起过多的组件重新绘制渲染,建议关联数量限制在20个以内,达到[精准控制组件的更新范围](precisely-control-render-scope.md)。
896
897**控制状态变量关联组件数量**
898
899反例代码如下:
900
901```typescript
902@Observed
903class Translate {
904  translateX: number = 20;
905}
906@Component
907struct Title {
908  @ObjectLink translateObj: Translate;
909  build() {
910    Row() {
911      Image($r('app.media.icon'))
912        .translate({
913          x: this.translateObj.translateX // this.translateObj.translateX used in two component both in Row
914        })
915      Text("Title")
916        .translate({
917          x: this.translateObj.translateX
918        })
919    }
920  }
921}
922@Entry
923@Component
924struct Page {
925  @State translateObj: Translate = new Translate();
926  build() {
927    Column() {
928      Title({
929        translateObj: this.translateObj
930      })
931      Stack() {
932      }
933      .translate({
934        x:this.translateObj.translateX // this.translateObj.translateX used in two components both in Column
935      })
936      Button("move")
937        .translate({
938          x: this.translateObj.translateX
939        })
940        .onClick(() => {
941          this.getUIContext().animateTo({
942            duration: 50
943          }, () => {
944            this.translateObj.translateX = (this.translateObj.translateX + 50) % 150;
945          })
946        })
947    }
948  }
949}
950```
951
952正例代码如下:
953
954```typescript
955@Observed
956class Translate {
957  translateX: number = 20;
958}
959@Component
960struct Title {
961  build() {
962    Row() {
963      Image($r('app.media.icon'))
964      Text("Title")
965    }
966  }
967}
968@Entry
969@Component
970struct Page1 {
971  @State translateObj: Translate = new Translate();
972  build() {
973    Column() {
974      Title()
975      Stack() {
976      }
977      Button("move")
978        .onClick(() => {
979          this.getUIContext().animateTo({
980            duration: 50
981          }, () => {
982            this.translateObj.translateX = (this.translateObj.translateX + 50) % 150;
983          })
984        })
985    }
986    .translate({ // the component in Column shares the same property translate
987      x: this.translateObj.translateX
988    })
989  }
990}
991```
992
993**控制对象级状态变量成员数量**
994
995应该控制对象级状态变量的成员变量关联的组件数量。开发者封装一个数据结构类用于进行状态变量关联时,应该避免过多的成员变量关联大量ArkUI组件,这种情况下,当这个大对象的一个成员变量更新时,会导致所有关联这个大对象的组件都同时进行刷新,造成不必要的性能损耗,从而影响帧率。
996
997反例代码如下:
998
999```typescript
1000@Observed
1001class AnimationParams {
1002  translateX: number = 0;
1003  translateY: number = 0;
1004  alpha: number = 1;
1005  rotationX: number = 0;
1006  rotationY: number = 0;
1007  centerX: number = 0;
1008  centerY: number = 0;
1009  angle: number = 0;
1010  scaleX: number = 1;
1011  scaleY: number = 1;
1012}
1013
1014@Entry
1015@Component
1016struct Page {
1017  @State animationParam: AnimationParams = new AnimationParams();
1018
1019  build() {
1020    Column() {
1021      Row() {
1022        Image($r('app.media.startIcon'))
1023          .translate({
1024            x: this.animationParam.translateX,
1025            y: this.animationParam.translateY
1026          })
1027          .rotate({
1028            x: this.animationParam.rotationX,
1029            y: this.animationParam.rotationY,
1030            centerX: this.animationParam.centerX,
1031            centerY: this.animationParam.centerY,
1032            angle: this.animationParam.angle
1033          })
1034          .opacity(this.animationParam.alpha)
1035          .scale({
1036            x: this.animationParam.scaleX,
1037            y: this.animationParam.scaleY,
1038            centerX: this.animationParam.centerX,
1039            centerY: this.animationParam.centerY
1040          })
1041          .animation({
1042            duration: 3000
1043          })
1044      }
1045
1046      Button('点击播放动画')
1047        .onClick(() => {
1048          this.animationParam.translateX = 300;
1049          this.animationParam.translateY = 200;
1050          this.animationParam.rotationX = 90;
1051          this.animationParam.rotationY = 90;
1052          this.animationParam.centerX = 20;
1053          this.animationParam.centerY = 20;
1054          this.animationParam.angle = 270;
1055          this.animationParam.alpha = 0.5;
1056          this.animationParam.scaleX = 3;
1057          this.animationParam.scaleY = 3;
1058        })
1059    }
1060  }
1061}
1062```
1063
1064正例代码如下:
1065
1066```typescript
1067@Observed
1068class RotationAnimationParams {
1069  rotationX: number = 0;
1070  rotationY: number = 0;
1071  centerX: number = 0;
1072  centerY: number = 0;
1073  angle: number = 0;
1074}
1075
1076@Observed
1077class TranslateAnimationParams {
1078  translateX: number = 0;
1079  translateY: number = 0;
1080}
1081
1082@Observed
1083class AlphaAnimationParams {
1084  alpha: number = 1;
1085}
1086
1087@Observed
1088class ScaleAnimationParams {
1089  scaleX: number = 1;
1090  scaleY: number = 1;
1091  centerX: number = 0;
1092  centerY: number = 0;
1093}
1094
1095@Entry
1096@Component
1097struct Page {
1098  @State rotationAnimation: RotationAnimationParams = new RotationAnimationParams();
1099  @State translateAnimation: TranslateAnimationParams = new TranslateAnimationParams();
1100  @State alphaAnimation: AlphaAnimationParams = new AlphaAnimationParams();
1101  @State scaleAnimation: ScaleAnimationParams = new ScaleAnimationParams();
1102
1103  build() {
1104    Column() {
1105      Row() {
1106        Image($r('app.media.startIcon'))
1107          .translate({
1108            x: this.translateAnimation.translateX,
1109            y: this.translateAnimation.translateY
1110          })
1111          .rotate({
1112            x: this.rotationAnimation.rotationX,
1113            y: this.rotationAnimation.rotationY,
1114            centerX: this.rotationAnimation.centerX,
1115            centerY: this.rotationAnimation.centerY,
1116            angle: this.rotationAnimation.angle
1117          })
1118          .opacity(this.alphaAnimation.alpha)
1119          .scale({
1120            x: this.scaleAnimation.scaleX,
1121            y: this.scaleAnimation.scaleY,
1122            centerX: this.scaleAnimation.centerX,
1123            centerY: this.scaleAnimation.centerY
1124          })
1125          .animation({
1126            duration: 3000
1127          })
1128      }
1129
1130      Button('点击播放动画')
1131        .onClick(() => {
1132          this.rotationAnimation.rotationX = 90;
1133          this.rotationAnimation.rotationY = 90;
1134          this.rotationAnimation.centerX = 20;
1135          this.rotationAnimation.centerY = 20;
1136          this.rotationAnimation.angle = 270;
1137
1138          this.translateAnimation.translateX = 300;
1139          this.translateAnimation.translateY = 200;
1140
1141          this.alphaAnimation.alpha = 0.5;
1142
1143          this.scaleAnimation.scaleX = 3;
1144          this.scaleAnimation.scaleY = 3;
1145          this.scaleAnimation.centerX = 20;
1146          this.scaleAnimation.centerY = 20;
1147        })
1148    }
1149  }
1150}
1151```
1152
1153### 避免不必要的创建和读取状态变量
1154
1155避免不必要的创建和读取状态变量,减少性能损耗。
1156
1157**删除冗余的状态变量标记**
1158
1159状态变量的管理有一定的开销,应在合理场景使用,普通的变量用状态变量标记可能会导致性能劣化。
1160
1161反例代码如下:
1162
1163```typescript
1164@Observed
1165class Translate {
1166  translateX: number = 20;
1167}
1168
1169@Entry
1170@Component
1171struct UnnecessaryState1 {
1172  @State translateObj: Translate = new Translate(); // 变量translateObj没有关联任何UI组件,不应该定义为状态变量
1173  @State buttonMsg: string = 'I am button'; // 变量buttonMsg没有关联任何UI组件,不应该定义为状态变量
1174
1175  build() {
1176  }
1177}
1178```
1179以上示例中变量translateObj、buttonMsg没有关联任何UI组件,不应该定义为状态变量,否则读写状态变量都会影响性能。
1180
1181```typescript
1182@Observed
1183class Translate {
1184  translateX: number = 20;
1185}
1186
1187@Entry
1188@Component
1189struct UnnecessaryState2 {
1190  @State buttonMsg: string = 'I am button';
1191
1192  build() {
1193    Column() {
1194      Button(this.buttonMsg) // 这里只是读取变量buttonMsg的值,没有任何写的操作
1195    }
1196  }
1197}
1198```
1199以上示例中变量buttonMsg仅有读取操作,没有修改过,没有修改过的状态变量不应该定义为状态变量,否则读状态变量会影响性能。
1200
1201正例代码如下:
1202
1203```typescript
1204@Observed
1205class Translate {
1206  translateX: number = 20;
1207}
1208
1209@Entry
1210@Component
1211struct NecessaryState {
1212  @State translateObj: Translate = new Translate(); // 同时存在读写操作,并关联了Button组件,推荐使用状态变量
1213  buttonMsg: string = 'I am button'; // 仅读取变量buttonMsg的值,没有任何写的操作,直接使用一般变量即可
1214
1215  build() {
1216    Column() {
1217      Button(this.buttonMsg)
1218        .onClick(() => {
1219          this.getUIContext().animateTo(
1220            {
1221              duration: 50
1222            }, () => {
1223            this.translateObj.translateX = (this.translateObj.translateX + 50) % 150; // 点击时给变量translateObj重新赋值
1224          })
1225        })
1226    }.translate({
1227      x:this.translateObj.translateX // 读取translateObj中的值
1228    })
1229  }
1230}
1231```
1232没有关联任何UI组件的状态变量和没有修改过的状态变量不应该定义为状态变量,直接使用一般变量即可,否则会影响性能。
1233
1234**避免在For/while等循环函数中重复读取状态变量**
1235
1236状态变量的读取耗时远大于普通变量的读取耗时,因此要避免重复读取状态变量,而是应该放在循环外面读取,例如在打印For/while循环中打印状态变量的日志信息。
1237
1238反例代码:
1239
1240```typescript
1241import hiTraceMeter from '@ohos.hiTraceMeter';
1242
1243@Entry
1244@Component
1245struct Page {
1246  @State message: string = '';
1247
1248  build() {
1249    Column() {
1250      Button('点击打印日志')
1251        .onClick(() => {
1252          hiTraceMeter.startTrace('print', 1);
1253          for (let i = 0; i < 10; i++) {
1254            console.info(this.message);
1255          }
1256          hiTraceMeter.finishTrace('print', 1);
1257        })
1258    }
1259  }
1260}
1261```
1262抓取Trace图如下:
1263![](./figures/unnecessarystate.png)
1264
1265正例代码:
1266
1267```typescript
1268import hiTraceMeter from '@ohos.hiTraceMeter';
1269
1270@Entry
1271@Component
1272struct Page {
1273  @State message: string = '';
1274
1275  build() {
1276    Column() {
1277      Button('点击打印日志')
1278        .onClick(() => {
1279          hiTraceMeter.startTrace('print', 1);
1280          let logMessage: string = this.message;
1281          for (let i = 0; i < 10; i++) {
1282            console.info(logMessage);
1283          }
1284          hiTraceMeter.finishTrace('print', 1);
1285        })
1286    }
1287  }
1288}
1289```
1290抓取Trace图如下:
1291![](./figures/necessarystate.png)
1292
1293由此可见,使用普通变量代替状态变量在For/while循环中读取,可以减少耗时,因此在For/while循环中频繁读取变量时,可使用普通变量代替状态变量。
1294## 第四要素:合理使用系统接口,避免冗余操作
1295
1296应该合理使用系统的高频回调接口,删除不必要的Trace和日志打印,避免冗余操作,减少系统开销,[避免开发过程中的冗余操作](avoiding-redundant-operations.md)。
1297
1298### 避免在系统高频回调用进行冗余和耗时操作
1299
1300应该避免在onDidScroll、onAreaChange等系统高频的回调接口中进行冗余和耗时操作,这些接口在系统的每一帧绘制中都会执行回调操作,因此在这些接口中进行冗余和耗时操作会大量消耗系统资源,影响应用运行性能。
1301
1302**避免在系统高频回调用打印Trace**
1303
1304Trace的打印是会额外消耗系统性能的,因此应该避免在这些系统高频回调接口中打印Trace,示例代码如下:
1305
1306```typescript
1307// 反例
1308import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
1309
1310@Component
1311struct NegativeOfOnDidScroll {
1312  private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
1313
1314  build() {
1315    Scroll() {
1316      ForEach(this.arr, (item: number) => {
1317        Text("ListItem" + item)
1318          .width("100%")
1319          .height("100%")
1320      }, (item: number) => item.toString())
1321    }
1322    .width('100%')
1323      .height('100%')
1324      .onDidScroll(() => {
1325        hiTraceMeter.startTrace("ScrollSlide", 1002);
1326        // 业务逻辑
1327        // ...
1328        hiTraceMeter.finishTrace("ScrollSlide", 1002);
1329      })
1330  }
1331}
1332
1333// 正例
1334@Component
1335struct PositiveOfOnDidScroll {
1336  private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
1337
1338  build() {
1339    Scroll() {
1340      List() {
1341        ForEach(this.arr, (item: number) => {
1342          ListItem() {
1343            Text("TextItem" + item)
1344          }
1345          .width("100%")
1346          .height(100)
1347        }, (item: number) => item.toString())
1348      }
1349      .divider({ strokeWidth: 3, color: Color.Gray })
1350    }
1351    .width('100%')
1352    .height('100%')
1353    .onDidScroll(() => {
1354      // 业务逻辑
1355      // ...
1356    })
1357  }
1358}
1359```
1360
1361**避免在系统高频回调用打印日志**
1362
1363日志的打印是会额外消耗系统性能的,特别是有些日志还读取了状态变量的信息,会加剧资源开销,因此应该避免在这些系统高频回调接口中打印日志,示例代码如下:
1364
1365```typescript
1366// 反例
1367import { hilog } from '@kit.PerformanceAnalysisKit';
1368
1369@Component
1370struct NegativeOfOnDidScroll {
1371  private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
1372
1373  build() {
1374    Scroll() {
1375      List() {
1376        ForEach(this.arr, (item: number) => {
1377          ListItem() {
1378            Text("TextItem" + item)
1379          }
1380          .width("100%")
1381          .height(100)
1382        }, (item: number) => item.toString())
1383      }
1384      .divider({ strokeWidth: 3, color: Color.Gray })
1385    }
1386    .width('100%')
1387    .height('100%')
1388    .onDidScroll(() => {
1389      hilog.info(1002, 'Scroll', 'TextItem');
1390      // 业务逻辑
1391      // ...
1392    })
1393  }
1394}
1395
1396// 正例
1397@Component
1398struct PositiveOfOnDidScroll {
1399  private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
1400
1401  build() {
1402    Scroll() {
1403      List() {
1404        ForEach(this.arr, (item: number) => {
1405          ListItem() {
1406            Text("TextItem" + item)
1407          }
1408          .width("100%")
1409          .height(100)
1410        }, (item: number) => item.toString())
1411      }
1412      .divider({ strokeWidth: 3, color: Color.Gray })
1413    }
1414    .width('100%')
1415    .height('100%')
1416    .onDidScroll(() => {
1417      // 业务逻辑
1418      // ...
1419    })
1420  }
1421}
1422```
1423
1424### 删除冗余Trace和日志打印
1425
1426Trace和日志打印会比较消耗系统性能,因此应该避免冗余的Trace和日志打印。推荐在Release版本中,尽量删除所有Trace信息,删除Debug日志,减少额外的系统开销。
1427
1428**在Release版本中删除Trace**
1429
1430Trace会比较消耗系统性能,建议在Release版本删除Trace打印。
1431
1432反例代码如下:
1433
1434```typescript
1435@Component
1436struct NegativeOfTrace {
1437  aboutToAppear(): void {
1438    hitrace.startTrace("HITRACE_TAG_APP", 1003);
1439    // 业务代码
1440    // ...
1441    hitrace.finishTrace("HITRACE_TAG_APP", 1003);
1442  }
1443  build() {
1444    // 业务代码
1445    // ...
1446  }
1447}
1448```
1449
1450正例代码如下:
1451
1452```typescript
1453@Component
1454struct PositiveOfTrace {
1455  aboutToAppear(): void {
1456    // 业务代码
1457    // ...
1458  }
1459  build() {
1460    // 业务代码
1461    // ...
1462  }
1463}
1464```
1465
1466**在Release版本中删除Debug日志**
1467
1468虽然在Release版本中不会打印debug级别日志,但是如果在日志的入参中进行了参数拼接,字符串拼接的逻辑还会执行,会有冗余开销,因此为了[避免开发过程中的冗余操作](avoiding-redundant-operations.md),建议在Release版本删除Debug日志打印。
1469
1470反例代码如下:
1471
1472```typescript
1473@Component
1474struct NegativeOfDebug {
1475  @State string1: string = 'a';
1476  @State string2: string = 'b';
1477
1478  aboutToAppear(): void {
1479    hilog.debug(1004, 'Debug', (this.string1 + this.string2));
1480    // 业务代码
1481    // ...
1482  }
1483
1484  build() {
1485    // 业务代码
1486    // ...
1487  }
1488}
1489```
1490
1491正例代码如下:
1492
1493```typescript
1494@Component
1495struct PositiveOfDebug {
1496  aboutToAppear(): void {
1497    // 业务代码
1498    // ...
1499  }
1500  build() {
1501    // 业务代码
1502    // ...
1503  }
1504}
1505```
1506
1507### 避免设置冗余系统回调监听
1508
1509冗余的系统回调监听,会额外消耗系统开销去做计算和函数回调消耗。比如设置了onAreaChange,就算回调中没有任何逻辑,系统也会在C++侧去计算该组件的大小和位置变化情况,并且把结果回调到TS侧,额外消耗了系统开销。
1510
1511反例代码如下:
1512
1513```typescript
1514@Component
1515struct NegativeOfOnClick {
1516  build() {
1517    Button('Click', { type: ButtonType.Normal, stateEffect: true })
1518      .onClick(() => {
1519        hitrace.startTrace("ButtonClick", 1004);
1520        hilog.info(1004, 'Click', 'ButtonType.Normal');
1521        hitrace.finishTrace("ButtonClick", 1004);
1522        // 业务代码
1523        // ...
1524      })
1525      .onAreaChange((oldValue: Area, newValue: Area) => {
1526        // 无任何代码
1527      })
1528  }
1529}
1530```
1531
1532正例代码如下:
1533
1534```typescript
1535@Component
1536struct PositiveOfOnClick {
1537  build() {
1538    Button('Click', { type: ButtonType.Normal, stateEffect: true })
1539      .onClick(() => {
1540        // 业务代码
1541        // ...
1542      })
1543  }
1544}
1545```
1546
1547## 使用性能工具分析和定位问题
1548
1549学会合理使用工具进行问题分析和定位,提升问题解决效率。
1550
1551### 学会使用IDE的Profier工具定位问题
1552
1553通过使用Profier工具,定位应用开发过程中的各种性能问题,详细的使用方法可以参考文章:[性能分析工具CPU Profiler](application-performance-analysis.md)。
1554
1555### 使用SmartPerf-Host分析应用性能
1556
1557[SmartPerf-Host](performance-optimization-using-smartperf-host.md)是一款深入挖掘数据、细粒度展示数据的性能功耗调优工具,可采集CPU调度、频点、进程线程时间片、堆内存、帧率等数据,采集的数据通过泳道图清晰地呈现给开发者,同时通过GUI以可视化的方式进行分析。工具当前为开发者提供了五个分析模板,分别是帧率分析、CPU/线程调度分析、应用启动分析、TaskPool分析、动效分析。
1558
1559### 使用状态变量组件定位工具分析状态变量关联信息
1560
1561开发者可以使用[状态变量组件定位工具](state_variable_dfx_pratice.md)获取状态管理相关信息,例如自定义组件拥有的状态变量、状态变量的同步对象和关联组件等,了解状态变量影响UI的范围,写出高性能应用代码。
1562
1563### 使用常用trace使用指导协助定位性能问题
1564
1565本文旨在介绍[常用trace使用指导](common-trace-using-instructions.md),解释它们的含义和用途,并阐述如何通过这些Trace来识别潜在的性能问题。同时,还将详细介绍Trace的工作原理,帮助开发者更好地理解这些Trace及如何实现性能数据的采集和分析。通过本文的阅读,开发者将对Trace有一个深入的了解,为应用程序性能优化提供有力支持。