性能分析四板斧

第三板斧:合理管理状态变量

应该合理地使用状态变量,精准控制组件的更新范围(详细介绍可参考文章: 精准控制组件的更新范围),控制状态变量关联组件数量上限,控制对象级状态变量的成员变量关联组件数,减少系统的组件渲染负载,提升应用流畅度。

精准控制组件的更新范围

在复杂页面开发的场景下,精准控制组件更新的范围对提高应用运行性能尤为重要。我们应该避免状态变量的滥用引起的容器组件的刷新,进而影响帧率。

使用Stack包裹条件渲染组件,减小更新范围

用条件渲染的方式控制组件的显示,在条件发生变化时会引起父组件的重新渲染。如果在同个父组件下存在复杂组件,在高频操作场景会产生性能问题。因此,对于该场景,我们应该用父容器组件包裹可能会频繁条件变化的组件

反例代码如下:

@State isVisible: boolean;
@State realData: Data[] = [];

aboutToAppear() {
  // 更新状态变量
  this.realData = [...]
}

build() {
  Column() {
    if(this.isVisible) {
      Text()
    }
    ForEach(this.realData,(item: Data) => {
      Text(`${item.label}`)
    })
  }
}

正例代码如下:

@State isVisible: boolean;
@State realData: Data[] = [];

aboutToAppear() {
  // 更新状态变量
  this.realData = [...]
}

build() {
  Column() {
    Stack() {
      if(this.isVisible) {
        Text()
      }
    }
    ForEach(this.realData,(item: Data) => {
      Text(`${item.label}`)
    })
  }
}

减少不必要的参数层次传递

@State+@Prop、@State+@Link、@State+@Observed+@ObjectLink三种方案的实现方式是逐级向下传递状态,当共享状态的组件间层级相差较大时,会出现状态层层传递的现象。对于没有使用该状态的中间组件而言,这是“额外的消耗”。因此,对于跨越多层的状态变量传递,使用@Provide+@Consume方案更为合理。

反例代码如下:

// 父组件
@Component
struct componentParent{
  @State data: Data = {}

  aboutToAppear() {
    // 获取子组件数据
    this.data = getData()
  }

  build() {
    Column() {
      componentSon({data: this.data})
    }
  }
}

// 子组件
@Component
struct componentSon{
  // 获取传递参数
  @Prop data: Data;

  build() {
    Column() {
      Text(data.text)
      componentGrandSon({data: this.data})
    }
  }
}

@Component
struct componentGrandSon{
  // 获取传递参数
  @Prop data: Data;

  build() {
    Column() {
      Text(data.text)
    }
  }
}

正例代码如下:

// 父组件
@Component
struct componentParent{
  @Provide('data') data: Data = {};

  aboutToAppear() {
    // 获取子组件数据
    this.data = getData()
  }

  build() {
    Column() {
      componentSon({data: this.data})
    }
  }
}

// 子组件
@Component
struct componentSon{
  // 获取传递参数
  @Consume("data") data: Data;

  build() {
    Column() {
      Text(data.text)
      componentGrandSon({data: this.data})
    }
  }
}

@Component
struct componentGrandSon{
  // 获取传递参数
  @Consume("data") data: Data;

  build() {
    Column() {
      Text(data.text)
    }
  }
}

避免滥用@Provide+@Consume

在父子组件关联的场景下,@Provide+@Consume开销要大于@State+@Prop/@Link,因此在该场景下推荐使用@State+@Prop/@Link的组合。

反例代码如下:

// 父组件
@Component
struct componentParent{
  @Provide("data") data: Data = {};

  aboutToAppear() {
    // 获取子组件数据
    this.data = getData();
  }

  build() {
    Column() {
      componentSon()
    }
  }
}

// 子组件
@Component
struct componentSon{
  // 获取传递参数
  @Consume("data") data: Data;

  build() {
    Column() {
      Text(data.text)
    }
  }
}

正例代码如下:

// 父组件
@Component
struct componentParent{
  @State data:Data = {};

  aboutToAppear() {
    // 获取子组件数据
    this.data = getData();
  }

  build() {
    Column() {
      componentSon({data: this.data})
    }
  }
}

// 子组件
@Component
struct componentSon{
  // 获取传递参数
  @Prop data:Data;

  build() {
    Column() {
      Text(data.text)
    }
  }
}

精准控制状态变量关联组件数量

应该控制状态变量关联的组件数量,如果一个状态关联过多的组件,当这个变量更新时会引起过多的组件重新绘制渲染,建议关联数量限制在20个以内(详细介绍可参考文章: 精准控制组件的更新范围)。

控制状态变量关联组件数量

反例代码如下:

@Observed
class Translate {
  translateX: number = 20;
}
@Component
struct Title {
  @ObjectLink translateObj: Translate;
  build() {
    Row() {
      Image($r('app.media.icon'))
        .translate({
          x:this.translateObj.translateX // this.translateObj.translateX used in two component both in Row
        })
      Text("Title")
        .translate({
          x: this.translateObj.translateX
        })
    }
  }
}
@Entry
@Component
struct Page {
  @State translateObj: Translate = new Translate();
  build() {
    Column() {
      Title({
        translateObj: this.translateObj
      })
      Stack() {
      }
      .translate({
        x:this.translateObj.translateX //this.translateObj.translateX used in two components both in Column
      })
      Button("move")
        .translate({
          x:this.translateObj.translateX
        })
        .onClick(() => {
          animateTo({
            duration: 50
          },()=>{
            this.translateObj.translateX = (this.translateObj.translateX + 50) % 150
          })
        })
    }
  }
}

正例代码如下:

@Observed
class Translate {
  translateX: number = 20;
}
@Component
struct Title {
  build() {
    Row() {
      Image($r('app.media.icon'))
      Text("Title")
    }
  }
}
@Entry
@Component
struct Page1 {
  @State translateObj: Translate = new Translate();
  build() {
    Column() {
      Title()
      Stack() {
      }
      Button("move")
        .onClick(() => {
          animateTo({
            duration: 50
          },()=>{
            this.translateObj.translateX = (this.translateObj.translateX + 50) % 150
          })
        })
    }
    .translate({ // the component in Column shares the same property translate
      x: this.translateObj.translateX
    })
  }
}

控制对象级状态变量成员数量

应该控制对象级状态变量的成员变量关联的组件数量。开发者封装一个数据结构类用于进行状态变量关联时,应该避免过多的成员变量关联大量ArkUI组件,这种情况下,当这个大对象的一个成员变量更新时,会导致所有关联这个大对象的组件都同时进行刷新,造成不必要的性能损耗,从而影响帧率。

反例代码如下:

@Observed
class AnimationParams {
  translateX: number = 0;
  translateY: number = 0;
  alpha: number = 1;
  rotationX: number = 0;
  rotationY: number = 0;
  centerX: number = 0;
  centerY: number = 0;
  angle: number = 0;
  scaleX: number = 1;
  scaleY: number = 1;
}

@Entry
@Component
struct Page {
  @State animationParam: AnimationParams = new AnimationParams();

  build() {
    Column() {
      Row() {
        Image($r('app.media.startIcon'))
          .translate({
            x: this.animationParam.translateX,
            y: this.animationParam.translateY
          })
          .rotate({
            x: this.animationParam.rotationX,
            y: this.animationParam.translateY,
            centerX: this.animationParam.centerX,
            centerY: this.animationParam.centerY,
            angle: this.animationParam.angle
          })
          .opacity(this.animationParam.alpha)
          .scale({
            x: this.animationParam.scaleX,
            y: this.animationParam.scaleY,
            centerX: this.animationParam.centerX,
            centerY: this.animationParam.centerY
          })
          .animation({
            duration: 3000
          })
      }

      Button('点击播放动画')
        .onClick(() => {
          this.animationParam.translateX = 300;
          this.animationParam.translateY = 200;
          this.animationParam.rotationX = 90;
          this.animationParam.rotationY = 90;
          this.animationParam.centerX = 20;
          this.animationParam.centerY = 20;
          this.animationParam.angle = 270;
          this.animationParam.alpha = 0.5;
          this.animationParam.scaleX = 3;
          this.animationParam.scaleY = 3;
        })
    }
  }
}

正例代码如下:

@Observed
class RotationAnimationParams {
  rotationX: number = 0;
  rotationY: number = 0;
  centerX: number = 0;
  centerY: number = 0;
  angle: number = 0;
}

@Observed
class TranslateAnimationParams {
  translateX: number = 0;
  translateY: number = 0;
}

@Observed
class AlphaAnimationParams {
  alpha: number = 1;
}

@Observed
class ScaleAnimationParams {
  scaleX: number = 1;
  scaleY: number = 1;
  centerX: number = 0;
  centerY: number = 0;
}

@Entry
@Component
struct Page {
  @State rotationAnimation: RotationAnimationParams = new RotationAnimationParams();
  @State translateAnimation: TranslateAnimationParams = new TranslateAnimationParams();
  @State alphaAnimation: AlphaAnimationParams = new AlphaAnimationParams();
  @State scaleAnimation: ScaleAnimationParams = new ScaleAnimationParams();

  build() {
    Column() {
      Row() {
        Image($r('app.media.startIcon'))
          .translate({
            x: this.translateAnimation.translateX,
            y: this.translateAnimation.translateY
          })
          .rotate({
            x: this.rotationAnimation.rotationX,
            y: this.rotationAnimation.rotationY,
            centerX: this.rotationAnimation.centerX,
            centerY: this.rotationAnimation.centerY,
            angle: this.rotationAnimation.angle
          })
          .opacity(this.alphaAnimation.alpha)
          .scale({
            x: this.scaleAnimation.scaleX,
            y: this.scaleAnimation.scaleY,
            centerX: this.scaleAnimation.centerX,
            centerY: this.scaleAnimation.centerY
          })
          .animation({
            duration: 3000
          })
      }

      Button('点击播放动画')
        .onClick(() => {
          this.rotationAnimation.rotationX = 90;
          this.rotationAnimation.rotationY = 90;
          this.rotationAnimation.centerX = 20;
          this.rotationAnimation.centerY = 20;
          this.rotationAnimation.angle = 270;

          this.translateAnimation.translateX = 300;
          this.translateAnimation.translateY = 200;

          this.alphaAnimation.alpha = 0.5;

          this.scaleAnimation.scaleX = 3;
          this.scaleAnimation.scaleY = 3;
          this.scaleAnimation.centerX = 20;
          this.scaleAnimation.centerY = 20;
        })
    }
  }
}

避免不必要的创建和读取状态变量

避免不必要的创建和读取状态变量,减少性能损耗。

删除冗余的状态变量标记

状态变量的管理有一定的开销,应在合理场景使用,普通的变量用状态变量标记可能会导致性能劣化

反例代码如下:

@Component
struct component {
  @State bgcolor: string | Color = '#ffffff';
  @State selectColor: string | Color = '#007DFF';

  build() {
  }
}

正例代码如下:

@Component
struct component {
  bgcolor: string | Color = '#ffffff';
  selectColor: string | Color = '#007DFF';

  build() {
  }
}

避免在For/while等循环函数中重复读取状态变量

状态变量的读取耗时远大于普通变量的读取耗时,因此要避免重复读取状态变量,而是应该放在循环外面读取,例如在打印For/while循环中打印状态变量的日志信息

反例代码如下:

@Component
struct Page {
  @State message: string = '';

  build() {
    Column() {
      Button('点击打印日志')
        .onClick(() => {
          for (let i = 0; i < 10; i++) {
            console.debug(this.message);
          }
        })
    }
  }
}

正例代码如下:

@Component
struct Page {
  @State message: string = '';

  build() {
    Column() {
      Button('点击打印日志')
        .onClick(() => {
          let logMessage: string = this.message;
          for (let i = 0; i < 10; i++) {
            console.debug(logMessage);
          }
        })
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值