# 应用性能优化常见问题解决指导
## 概述
本文总结了实际开发应用时常见的性能优化规范,配合举例实际开发中常见的正反例代码,帮助开发者解决大部分性能问题。
### 性能规范总览目录
|
分类
|
高频程度 (5满分)
| 规范(检查项) | 实操方法 |
代码示例
|
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------:|:---------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------:|
| 响应时延 / 完成时延 | 5 | 不建议在aboutToAppear(),aboutToDisappear()等生命周期中执行耗时操作 | 排查所有的aboutToAppear和aboutToDisappear函数(或者通过Trace查看),查看是否有耗时操作,改为setTimeOut或者在TaskPool中执行。 | [代码示例](#不建议在abouttoappearabouttodisappear等生命周期中执行耗时操作) |
| 响应时延 / 完成时延 | 5 | 不要在回调函数中执行耗时操作(ArkUI接口回调、网络访问回调、await等) | 排查所有的回调函数(或者通过Trace查看),尤其是ArkUI接口,网络回调函数,查看是否有耗时操作,是否使用了await操作,改为setTimeOut或者在TaskPool中执行。 | [代码示例](#不要在回调函数中执行耗时操作arkui接口回调网络访问回调await等) |
| 响应时延 / 完成时延 / 帧率 | 5 | 列表场景未使用LazyForEach+组件复用+缓存列表项 | 排查使用LazyForEach的代码,确认是否有使用组件复用(@Reusable)+缓存列表项(cachedCount)。 | [代码示例](#列表场景未使用lazyforeach组件复用缓存列表项) |
| 完成时延 | 5 | Web未使用预连接,未提前初始化引擎 | 在应用创建Ability的时候,在OnCreate阶段预先初始化内核,建议把引擎的初始化放在setTimeOut中。 | [代码示例](#web未使用预连接未提前初始化引擎) |
| 响应时延 / 完成时延 | 5 | 高频接口中不要打印Trace和日志 | 排查接口onTouch、onItemDragMove、onDragMove、onDidScroll、onMouse、onVisibleAreaChange、OnAreaChange、onActionUpdate、animator的onFrame、组件复用场景下的aboutToReuse,不建议在里面打印trace和日志。 | [代码示例](#高频接口中不要打印trace和日志) |
| 完成时延 / 帧率 | 4 | 组件复用里面有if语句,但是未使用reuseId | 排查使用了@Reusable的自定义组件,查看build中给是否使用了if/else或ForEach等条件渲染语句,如果使用了,需要配合reuseId一起使用。 | [代码示例](#组件复用里面有if语句但是未使用reuseid) |
| 响应时延 / 完成时延 | 4 | 不建议使用@Prop装饰器 | 全局搜索@Prop并且替换 | [代码示例](#不建议使用prop装饰器) |
| 响应时延 / 完成时延 | 3 | 避免在ResourceManager的getXXXSync接口入参中直接使用资源信息 | 排查ResourceManager.getXXXSync接口,查看入参时需要使用getStringSync($r('app.media.icon').id)的形式,如果未使用需要整改。 | [代码示例](#避免在resourcemanager的getxxxsync接口入参中直接使用资源信息) |
| 响应时延 / 完成时延 | 3 | 展示用的自定义组件(数据从父组件中获取,无独立数据处理)使用@Builder替换 | 审视@Component标记的自定义组件,如果里面没有独立的生命周期处理逻辑,数据由父组件传递,建议@Builder替代。 | [代码示例](#展示用的自定义组件数据从父组件中获取无独立数据处理使用builder替换) |
| 响应时延 / 完成时延 / 帧率 | 3 | 删除无具体逻辑的生命周期,ArkUI的函数回调等,删除冗余堵塞日志打印 | 排查所有的aboutToAppear、aboutToDisappear等生命周期函数,排查ArkUI的回调函数,如果函数中无具体业务逻辑,例如只打印了日志,删除函数回调。 | [代码示例](#删除无具体逻辑的生命周期arkui的函数回调等删除冗余堵塞日志打印) |
| 响应时延 / 完成时延 | 3 | 删除未关联组件的状态变量装饰器 | 排查全局的状态变量装饰器,如果变量未关联组件,删除装饰器。 | [代码示例](#删除未关联组件的状态变量装饰器) |
| 帧率 | 2 | crypto-js性能差 | 排查buffer.from关键字,加密建议使用原生的cryptoFramework,然后将buffer替换为base64helper,性能提升10倍以上, 且数据量越大越明显。 | [代码示例](#crypto-js性能差) |
| 响应时延 / 完成时延 | 1 | 不建议使用Marquee组件 | 排查Marquee关键字,使用Text的跑马灯模式(TextOverflow.MARQUEE)替代。 | [代码示例](#不建议使用marquee组件) |
| 完成时延 | 1 | 不能使用函数作为ArkUI组件的属性和组件复用的自定义组件的入参 | 查看属性是否有xx()函数写法,确认函数/方法中是否有耗时操作,替换成变量。 | [代码示例](#不能使用函数作为arkui组件的属性和组件复用的自定义组件的入参) |
| 完成时延 | 1 | 不建议使用.linearGradient颜色渐变属性 | 排查linearGradient关键字,可以使用图片代替。 | [代码示例](#不建议使用lineargradient颜色渐变属性) |
| 完成时延 / 帧率 | 1 | 不要在for/while循环中执行耗时操作 | 排查for/while循环,查看里面是否有打印日志或者Trace。 | [代码示例](#不要在forwhile循环中执行耗时操作) |
| 完成时延 / 帧率 | 1 | 变量初值不建议设置为undefined,需进行默认初始化 | 例如number设置为0,string设置为空字符串等,这样在使用过程中更不需要增加额外判空。排查类中的变量,看看是否有初始化为undefined。 | [代码示例](#变量初值不建议设置为undefined需进行默认初始化) |
## 性能优化规范
### 不建议在aboutToAppear()、aboutToDisappear()等生命周期中执行耗时操作
#### 类型
响应时延/完成时延
#### 解决方法
排查所有的aboutToAppear和aboutToDisappear函数(或者通过Trace查看),查看是否有耗时操作,改为setTimeOut或者在TaskPool中执行。
#### 反例
```typescript
@Entry
@Component
struct ViewA {
@State private text: string = "";
private count: number = 0;
// 反例:在aboutToAppear接口中执行耗时操作,阻塞页面绘制。
aboutToAppear() {
// 耗时操作
this.computeTask();
let context = context.resourceManager.getStringSync($r('app.string.startup_text'));
}
computeTask(): void {
this.count = 0;
while (this.count < LARGE_NUMBER) {
this.count++;
}
let context = getContext(this) as Context;
this.text = context.resourceManager.getStringSync($r('app.string.task_text'));
}
}
```
#### 正例
```typescript
@Entry
@Component
struct ViewB {
@State private text: string = "";
private count: number = 0;
private readonly DELAYED_TIME: number = 2000; // 定时器设置延时2s
// 正例:在aboutToAppear接口中对耗时间的计算任务进行了异步处理。
aboutToAppear() {
// 耗时操作
this.computeTaskAsync(); // 异步任务
let context = getContext(this) as Context;
this.text = context.resourceManager.getStringSync($r('app.string.startup_text'));
}
computeTask(): void {
this.count = 0;
while (this.count < LARGE_NUMBER) {
this.count++;
}
let context = getContext(this) as Context;
this.text = context.resourceManager.getStringSync($r('app.string.task_text'));
}
// 运算任务异步处理
private computeTaskAsync(): void {
setTimeout(() => {
// 这里使用setTimeout来实现异步延迟运行
this.computeTask();
}, DELAYED_TIME)
}
}
```
#### 高频程度&收益(5满分)
5
### 不要在回调函数中执行耗时操作(ArkUI接口回调、网络访问回调、await等)
#### 类型
响应时延/完成时延
#### 解决方法
排查所有的回调函数(或者通过Trace查看),尤其是ArkUI接口,网络回调函数,查看是否有耗时操作,是否使用了await操作,改为setTimeOut或者在TaskPool中执行。
#### 反例
```typescript
import http from '@ohos.net.http';
async aboutToAppear() {
// ...
const b = await http.createHttp();
}
```
#### 正例
```typescript
aboutToAppear() {
// ...
// 在生命周期中,使用TaskPool加载和解析网络数据
this.requestByTaskPool();
}
@Concurrent
getInfoFromHttp(): string[] {
// 从网络加载数据
return http.request();
}
requestByTaskPool(): void {
// 创建任务项
let task: taskpool.Task = new taskpool.Task(this.getInfoFromHttp);
try {
// 执行网络加载函数
taskpool.execute(task, taskpool.Priority.HIGH).then((res: string[]) => {
});
} catch (err) {
logger.error(TAG, "failed, " + (err as BusinessError).toString());
}
}
```
#### 高频程度&收益(5满分)
5
### 列表场景未使用LazyForEach+组件复用+缓存列表项
#### 类型
响应时延/完成时延/帧率
#### 解决方法
排查使用LazyForEach的代码,确认是否有使用组件复用(@Reusable)+缓存列表项(cachedCount)。
#### 反例
```typescript
struct GoodView {
build() {
Grid() {
// 未使用LazyForEach+组件复用+缓存列表项
ForEach(this.GoodDataOne, (item, index) => {
GridItem() {
Column() {
Image(item.img)
.height(item.hei)
.width('100%')
.objectFit(ImageFit.Fill)
Text(item.introduce)
.fontSize(14)
.padding({ left: 5, right: 5 })
.margin({ top: 5 })
Row() {
Row() {
Text('¥')
.fontSize(10)
.fontColor(Color.Red)
.baselineOffset(-4)
Text(item.price)
.fontSize(16)
.fontColor(Color.Red)
Text(item.numb)
.fontSize(10)
.fontColor(Color.Gray)
.baselineOffset(-4)
.margin({ left: 5 })
}
Image($r('app.media.photo63'))
.width(20)
.height(10)
.margin({ bottom: -8 })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding({ left: 5, right: 5 })
.margin({ top: 15 })
}
.borderRadius(10)
.backgroundColor(Color.White)
.clip(true)
.width('100%')
.height(290)
}
}, (item) => JSON.stringify(item))
}
}
}
```
#### 正例
```typescript
// 组件复用
@Reusable
@Component
struct GoodItems {
@State img: Resource = $r("app.media.photo61");
@State webImg?: string = '';
@State hei: number = 0;
@State introduce: string = '';
@State price: string = '';
@State numb: string = '';
@LocalStorageLink('storageSimpleProp') simpleVarName: string = '';
isOnclick: boolean = true;
index: number = 0;
controllerVideo: VideoController = new VideoController();
aboutToReuse(params)
{
this.webImg = params.webImg;
this.img = params.img;
this.hei = params.hei;
this.introduce = params.introduce;
this.price = params.price;
this.numb = params.numb;
}
build() {
Grid(){
// 懒加载
LazyForEach(this.GoodDataOne, (item, index) => {
GridItem() {
GoodItems({
isOnclick:item.data.isOnclick,
img:item.data.img,
webImg:item.data.webImg,
hei:item.data.hei,
introduce:item.data.introduce,
price:item.data.price,
numb:item.data.numb,
index:index
})
.reuseId(this.CombineStr(item.type))
}
}, (item) => JSON.stringify(item))
}.cachedCount(2) // 缓存列表项
}
}
```
#### 高频程度&收益(5满分)
5
### Web未使用预连接,未提前初始化引擎
#### 类型
完成时延
#### 解决方法
在应用创建Ability的时候,在OnCreate阶段预先初始化内核,建议把引擎的初始化放在setTimeOut中。
#### 反例
```typescript
// Web组件引擎没有初始化,且沒有使用预连接
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
}
controller: webview.WebviewController = new webview.WebviewController();
// ...
Web({ src: 'https://www.example.com', controller: this.controller })
```
#### 正例
```typescript
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
console.info("EntryAbility onCreate")
// 在 Web 组件初始化之前,通过此接口加载 Web 引擎的动态库文件,以提高启动性能。
setTimeout(() => {
// 这里使用setTimeout来实现延迟运行
web_webview.WebviewController.initializeWebEngine()
}, 200)
console.info("EntryAbility onCreate done");
}
}
controller: webview.WebviewController = new webview.WebviewController();
// ...
Web({ src: 'https://www.example.com', controller: this.controller })
```
#### 高频程度&收益(5满分)
5
### 高频接口中不要打印Trace和日志
#### 类型
响应时延/完成时延
#### 解决方法
排查接口onTouch、onItemDragMove、onDragMove、onDidScroll、onMouse、onVisibleAreaChange、OnAreaChange、
onActionUpdate、animator的onFrame、组件复用场景下的aboutToReuse,不建议在里面打印trace和日志。
#### 反例
```typescript
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
@Component
struct CounterOfOnDidScroll {
private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
build() {
Scroll() {
ForEach(this.arr, (item: number) => {
Text("ListItem" + item)
.width("100%")
.height("100%")
}, (item: number) => item.toString())
}
.width('100%')
.height('100%')
.onDidScroll(() => {
hiTraceMeter.startTrace("ScrollSlide", 1002);
// 业务逻辑
// ...
// 在高频接口中不建议打印Trace和日志
hiTraceMeter.finishTrace("ScrollSlide", 1002);
})
}
```
#### 正例
```typescript
@Component
struct PositiveOfOnDidScroll {
private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
build() {
Scroll() {
List() {
ForEach(this.arr, (item: number) => {
ListItem() {
Text("TextItem" + item)
}.width("100%")
.height(100)
}, (item: number) => item.toString())
}
.divider({ strokeWidth: 3, color: Color.Gray })
}
.width('100%')
.height('100%')
.onDidScroll(() => {
// 业务逻辑
// ...
})
}
}
```
#### 高频程度&收益(5满分)
4
### 组件复用里面有if语句,但是未使用reuseId
#### 类型
完成时延/帧率
#### 解决方法
排查使用了@Reusable的自定义组件,查看build中给是否使用了if/else或ForEach等条件渲染语句,如果使用了,需要配合reuseId一起使用。
#### 反例
```typescript
@Component
@Reusable
export struct MockComplexSubBranch {
@State alignStyle: FlexAlign = FlexAlign.Center;
aboutToReuse(params: Record): void { // 缓存复用组件,更新组件的状态变量
this.alignStyle = params.alignStyle;
}
build() {
Column() {
Column({ space: 5 }) {
Text('ComplexSubBranch not reusable')
.fontSize($r('app.integer.font_size_9'))
.fontColor($r('app.color.hint_txt_color'))
.width($r('app.string.layout_90_percent'))
}
}
}
}
import { MockComplexSubBranch } from './MockComplexSubBranch';
@Component
export struct WithoutReuseId {
@State isAlignStyleStart: boolean = true;
build() {
Column() {
Button("Change FlexAlign")
.onClick(() => {
this.isAlignStyleStart = !this.isAlignStyleStart;
})
Stack() {
if (this.isAlignStyleStart) {
MockComplexSubBranch({ alignStyle: FlexAlign.Start }); // 未使用reuseId
} else {
MockComplexSubBranch({ alignStyle: FlexAlign.End });
}
}
}
}
}
```
#### 正例
```typescript
@Component
@Reusable
// 添加Reusable装饰器,声明组件具备可复用的能力
export struct MockComplexSubBranch {
@State alignStyle: FlexAlign = FlexAlign.Center;
aboutToReuse(params: Record): void {
this.alignStyle = params.alignStyle;
}
build() {
Column() {
Column({ space: 5 }) {
Text('ComplexSubBranch reusable')
.fontSize($r('app.integer.font_size_9'))
.fontColor($r('app.color.hint_txt_color'))
.width($r('app.string.layout_90_percent'))
}
}
}
}
import { MockComplexReusableSubBranch } from './MockComplexReusableSubBranch';
@Component
export struct WithReuseId {
@State isAlignStyleStart: boolean = true;
build() {
Column() {
Button("Change FlexAlign")
.onClick(() => {
this.isAlignStyleStart = !this.isAlignStyleStart;
})
Stack() {
if (this.isAlignStyleStart) {
MockComplexSubBranch({ alignStyle: FlexAlign.Start }).reuseId("MockComplexSubBranchStart"); // 使用reuseId标识
} else {
MockComplexSubBranch({ alignStyle: FlexAlign.End }).reuseId("MockComplexSubBranchEnd");
}
}
}
}
}
```
#### 高频程度&收益(5满分)
4
### 不建议使用@Prop装饰器
#### 类型
响应时延/完成时延
#### 解决方法
全局搜索@Prop并且替换。
#### 反例
```typescript
@Observed
class Book {
public c: number = 0;
constructor(c: number) {
this.c = c;
}
}
@Component
struct PropChild {
@Prop testNum: Book; // @Prop装饰状态变量会深拷贝
build() {
Text(`PropChild testNum ${this.testNum.c}`)
}
}
@Entry
@Component
struct Parent1 {
@State testNum: Book[] = [new Book(1)];
build() {
Column() {
Text(`Parent testNum ${this.testNum[0].c}`)
.onClick(() => {
this.testNum[0].c += 1;
})
// PropChild没有改变@Prop testNum: Book的值,所以这时最优的选择是使用@ObjectLink
PropChild({ testNum: this.testNum[0] })
}
}
}
```
#### 正例
```typescript
@Observed
class Book {
public c: number = 0;
constructor(c: number) {
this.c = c;
}
}
@Component
struct PropChild {
@ObjectLink testNum: Book; // @ObjectLink装饰状态变量不会深拷贝
build() {
Text(`PropChild testNum ${this.testNum.c}`)
}
}
@Entry
@Component
struct Parent2 {
@State testNum: Book[] = [new Book(1)];
build() {
Column() {
Text(`Parent testNum ${this.testNum[0].c}`)
.onClick(() => {
this.testNum[0].c += 1;
})
// 当子组件不需要发生本地改变时,优先使用 @ObjectLink,因为@Prop是会深拷贝数据,具有拷贝的性能开销,所以这个时候@ObjectLink是比@Link和 @Prop更优的选择
PropChild({ testNum: this.testNum[0] })
}
}
}
```
#### 高频程度&收益(5满分)
4
### 避免在ResourceManager的getXXXSync接口入参中直接使用资源信息
#### 类型
响应时延/完成时延
#### 解决方法
排查ResourceManager.getXXXSync接口,查看入参时需要使用getStringSync($r('app.media.icon').id)的形式,
如果未使用需要整改。
#### 反例
```typescript
this.context.resourceManager.getStringSync($r('app.string.test'));
```
#### 正例
```typescript
this.context.resourceManager.getStringSync($r('app.string.test').id);
```
#### 高频程度&收益(5满分)
3
### 展示用的自定义组件(数据从父组件中获取,无独立数据处理)使用@Builder替换
#### 类型
响应时延/完成时延
#### 解决方法
审视@Component标记的自定义组件,如果里面没有独立的生命周期处理逻辑,数据由父组件传递,建议@Builder替代。
#### 反例
```typescript
@Entry
@Component
struct CEMineButtomView {
build() {
View();
}
}
@Component
export struct ViewA {
build() {
Row() {
Text('- 到底了 -')
.fontSize(12)
.fontColor($r("app.color.color_1"))
}.justifyContent(FlexAlign.Center)
.width('100%')
.height(51)
.padding({ bottom: 21 })
}
}
```
#### 正例
```typescript
@Builder
function viewB() {
Row() {
Text('- 到底了 -').fontSize(12)
.fontColor($r("app.color.color_1"))
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height(51)
.padding({ bottom: 21 })
}
@Entry
@Component
struct CEMineButtomView {
build() {
Column(){
viewB()
}.width('100%')
}
}
```
#### 高频程度&收益(5满分)
3
### 删除无具体逻辑的生命周期,ArkUI的函数回调等,删除冗余堵塞日志打印
#### 类型
响应时延/完成时延/帧率
#### 解决方法
排查所有的aboutToAppear、aboutToDisappear等生命周期函数,排查ArkUI的回调函数,如果函数中无具体业务逻辑,
例如只打印了日志,删除函数回调。
#### 反例
```typescript
import promptAction from '@ohos.promptAction';
@Entry
@Component
struct ViewA {
aboutToAppear(): void {
hilog.info('Index.ets aboutToAppear') // 无具体业务逻辑的日志
}
aboutToDisappear(): void{
hilog.info('Index.ets aboutToDisappear') // 无具体业务逻辑的日志
}
/**
* 弹窗函数
*/
showToast() {
promptAction.showToast({
message: $r('app.string.water_mark_toast_message')
})
}
build() {
Column(){
Text('测试一下')
.onClick(() => {
this.showToast(); // 有业务逻辑的方法
})
}.width('100%')
}
}
```
#### 正例
```typescript
import promptAction from '@ohos.promptAction';
@Entry
@Component
struct ViewB {
/**
* 弹窗函数
*/
showToast() {
promptAction.showToast({
message: $r('app.string.water_mark_toast_message')
})
}
build() {
Column(){
Text('测试一下')
.onClick(() => {
this.showToast(); // 有业务逻辑的方法
})
}.width('100%')
}
}
```
#### 高频程度&收益(5满分)
3
### 删除未关联组件的状态变量装饰器
#### 类型
响应时延/完成时延
#### 解决方法
排查全局的状态变量装饰器,如果变量未关联组件,删除装饰器。
#### 反例
```typescript
@Component
struct ComponentA {
@State message: string = 'Hello World';
@State textColor: string | Color = '#007DFF';
@State bgcolor: string | Color = '#ffffff'; // 变量bgcolor是没有关联组件的
@State selectColor: string | Color = '#007DFF'; // 变量selectColor是没有关联组件的
build() {
Column(){
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.fontColor(this.textColor)
}
}
}
```
#### 正例
```typescript
@Component
struct ComponentB {
@State message: string = 'Hello World';
@State textColor: string | Color = '#007DFF';
bgcolor: string | Color = '#ffffff'; // 变量bgcolor是有关联组件的
selectColor: string | Color = '#007DFF'; // 变量selectColor是有关联组件的
build() {
Column(){
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.fontColor(this.selectColor)
.backgroundColor(this.bgcolor)
}
}
}
```
#### 高频程度&收益(5满分)
2
### crypto-js性能差
#### 类型
帧率
#### 解决方法
排查buffer.from关键字,加密建议使用原生的cryptoFramework,然后将buffer替换为base64helper,性能提升10倍以上,
且数据量越大越明显。
#### 反例
```typescript
new Uint8Array(buffer.from(str,'base64').buffer);
```
#### 正例
```typescript
let that = new util.Base64Helper();
let result = that.decodeSync(str);
```
#### 高频程度&收益(5满分)
2
### 不建议使用Marquee组件
#### 类型
响应时延/完成时延
#### 解决方法
排查Marquee关键字,使用Text的跑马灯模式(TextOverflow.MARQUEE)替代。
#### 反例
```typescript
struct ViewA {
build() {
Column() {
Marquee({
start: this.start,
step: this.step,
loop: this.loop,
fromStart: this.fromStart,
src: this.src
})
.width(360)
.height(80)
.fontColor('#FFFFFF')
.fontSize(48)
.fontWeight(700)
.backgroundColor('#182431')
.margin({ bottom: 40 })
.onStart(() => {
console.info('Marquee animation complete onStart')
})
.onBounce(() => {
console.info('Marquee animation complete onBounce')
})
.onFinish(() => {
console.info('Marquee animation complete onFinish')
})
}.width("100%")
}
}
```
#### 正例
```typescript
struct ViewB {
build(){
Column(){
Text(reply.user)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.MARQUEE }) // 跑马灯模式
.width("30%")
}.width("100%")
}
}
```
#### 高频程度&收益(5满分)
1
### 不能使用函数作为ArkUI组件的属性和组件复用的自定义组件的入参
#### 类型
完成时延
#### 解决方法
查看属性是否有xx()函数写法,确认函数/方法中是否有耗时操作,替换成变量。
#### 反例
```typescript
struct ViewA {
build() {
Column() {
List() {
LazyForEach(this.data, (item: string) => {
ListItem() {
// 此处sum参数是函数获取的,每次组件复用都会重复触发此函数的调用
ChildComponent({ desc: item, sum: this.count() })
}.width('100%').height(100)
}, (item: string) => item)
}
}
}
}
```
#### 正例
```typescript
struct ViewB {
@State sum: number = 0;
aboutToAppear(): void {
this.sum = this.count();
}
build() {
Column() {
List() {
LazyForEach(this.data, (item: string) => {
ListItem() {
ChildComponent({ desc: item, sum: this.sum })
}.width('100%').height(100)
}, (item: string) => item)
}
}
}
}
```
#### 高频程度&收益(5满分)
1
### 不建议使用.linearGradient颜色渐变属性
#### 类型
完成时延
#### 解决方法
排查linearGradient关键字,可以使用图片代替。
#### 反例
```typescript
Row()
.linearGradient({
angle: 90,
colors: [[0xff0000, 0.0], [0x0000ff, 0.3], [0xffff00, 1.0]]
})
```
#### 正例
```typescript
Image($r('app.media.gradient_color'))
```
#### 高频程度&收益(5满分)
1
### 不要在for/while循环中执行耗时操作
#### 类型
完成时延/帧率
#### 解决方法
排查for/while循环,查看里面是否有打印日志或者Trace。
#### 反例
```typescript
@Component
struct ViewA {
@State message: string = "";
build() {
Column() {
Button('点击打印日志').onClick(() => {
for (let i = 0; i < 10; i++) {
console.info(this.message);
}
})
}
}
}
```
#### 正例
```typescript
@Component
struct ViewB {
@State message: string = "";
build() {
Column() {
Button('点击打印日志').onClick(() => {
let logMessage: string = this.message;
for (let i = 0; i < 10; i++) {
console.info(logMessage); // 状态变量需先赋值,再调用会优化性能
}
})
}
}
}
```
#### 高频程度&收益(5满分)
1
### 变量初值不建议设置为undefined,需进行默认初始化
#### 类型
完成时延
#### 解决方法
例如number设置为0,string设置为空字符串等,这样在使用过程中更不需要增加额外判空。
排查类中的变量,看看是否有初始化为undefined。
#### 反例
```typescript
@State channels?: Channels[] = undefined;
```
#### 正例
```typescript
@State channels?: Channels[] = [];
```
#### 高频程度&收益(5满分)
1