鸿蒙NEXT开发【状态管理最佳实践】性能分析

状态管理最佳实践概述

在声明式UI编程范式中,UI是应用程序状态的函数,应用程序状态的修改会更新相应的UI界面。ArkUI采用了[MVVM]模式,其中ViewModel将数据与视图绑定在一起,更新数据的时候直接更新视图。如下图所示:

图1 ArkUI的MVVM模式

1

ArkUI提供了一系列装饰器实现ViewModel的能力,如[@Prop]、[@Link]、[@Provide]、[LocalStorage]等。当自定义组件内变量被装饰器装饰时变为状态变量,状态变量的改变会引起UI的渲染刷新。

在ArkUI的开发过程中,如果没有选择合适的装饰器或合理的控制状态更新范围,可能会导致以下问题:

  1. 状态和UI的不一致,如同一状态的界面元素展示的UI不同,或UI界面展示的不是最新的状态。

  2. 非必要的UI视图刷新,如只修改局部组件状态时导致组件所在页面的整体刷新。

当用户与界面产生交互行为时,状态的修改是通过事件驱动处理的。事件的处理可以在应用的任何地方,如果没有进行适当的逻辑处理管理也会导致代码冗余和不利于维护。

本文旨在从装饰器的选择、使用以及状态的逻辑处理管理方面解决以上问题,以实现更好的状态管理。

合理选择装饰器

避免不必要的状态变量的使用

删除冗余的状态变量标记

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

反例1

@Observed
class Translate {
  translateX: number = 20;
}
@Entry
@Component
struct MyComponent {
  @State translateObj: Translate = new Translate(); // 变量translateObj没有关联任何UI组件,不应该定义为状态变量
  @State buttonMsg: string = 'I am button'; // 变量buttonMsg没有关联任何UI组件,不应该定义为状态变量
  build() {
  }
}

以上示例中变量translateObj,buttonMsg没有关联任何UI组件,没有关联任何UI组件的状态变量不应该定义为状态变量,否则读写状态变量都会影响性能。

反例2

@Observed
class Translate {
  translateX: number = 20;
}
@Entry
@Component
struct MyComponent {
  @State translateObj: Translate = new Translate();
  @State buttonMsg: string = 'I am button';
  build() {
    Column() {
      Button(this.buttonMsg) // 这里只是读取变量buttonMsg的值,没有任何写的操作
    }
  }
}

以上示例中变量buttonMsg仅有读取操作,没有修改过,没有修改过的状态变量不应该定义为状态变量,否则读状态变量会影响性能。

正例

@Observed
class Translate {
  translateX: number = 20;
}

@Entry
@Component
struct UnnecessaryState1 {
  @State translateObj: Translate = new Translate(); // 同时存在读写操作,并关联了Button组件,推荐使用状态变量
  buttonMsg = 'I am button'; // 仅读取变量buttonMsg的值,没有任何写的操作,直接使用一般变量即可

  build() {
    Column() {
      Button(this.buttonMsg)
        .onClick(() => {
          animateTo({
            duration: 50
          }, () => {
            this.translateObj.translateX = (this.translateObj.translateX + 50) % 150; // 点击时给变量translateObj重新赋值
          })
        })
    }
    .translate({
      x: this.translateObj.translateX // 读取translateObj中的值
    })
  }
}

没有关联任何UI组件的状态变量和没有修改过的状态变量不应该定义为状态变量,直接使用一般变量即可,否则会影响性能。

建议使用临时变量替换状态变量

状态变量发生变化时,ArkUI会查询依赖该状态变量的组件并执行依赖该状态变量的组件的更新方法,完成组件渲染的行为。通过使用临时变量的计算代替直接操作状态变量,可以使ArkUI仅在最后一次状态变量变更时查询并渲染组件,减少不必要的行为,从而提高应用性能。

反例

@Entry
@Component
struct Index {
  @State message: string = '';

  appendMsg(newMsg: string) {
    this.message += newMsg;
    this.message += ';';
    this.message += '<br/>';
  }

  build() {
    Column() {
      Button('点击打印日志')
        .onClick(() => {
          this.appendMsg('操作状态变量');
        })
        .width('90%')
        .backgroundColor(Color.Blue)
        .fontColor(Color.White)
        .margin({ top: 10})
    }
    .justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Center)
    .margin({  top: 15 })
  }
}

正例

@Entry
@Component
struct UnnecessaryState2 {
  @State message: string = '';

  appendMsg(newMsg: string) {
    let message = this.message;
    message += newMsg;
    message += ';';
    message += '<br/>';
    this.message = message;
  }

  build() {
    Column() {
      Button('点击打印日志')
        .onClick(() => {
          this.appendMsg('操作临时变量');
        })
        .width('90%')
        .backgroundColor(Color.Blue)
        .fontColor(Color.White)
        .margin({ top: 10 })
    }
    .justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Center)
    .margin({ top: 15 })
  }
}

最小化状态共享范围

在没有强烈的业务需求下,尽可能按照状态需要共享的最小范围选择合适的装饰器。应用开发过程中,按照组件颗粒度,状态一般分为组件内独享的状态和组件间需要共享的状态。

组件内独享的状态

组件内独享的状态的生命周期和组件同步,状态的定义和更新都在组件内,组件销毁,状态也随即消失。常见于界面UI元素数据,比如当前按钮是否可用、文字是否高亮等。组件内独享的状态使用[@State装饰器],被@State装饰器修饰后状态的修改只会触发当前组件实例的重新渲染。如下图主题列表上单个主题组件内使用@State修饰主题是否被选中的变量,当在界面点击主题时在组件内直接修改状态值。此时,只有当前主题的组件实例会重新渲染,其他主题组件不会重新渲染。

图2 HMOS世界App主题选择交互图

2

组件间需要共享的状态

组件间需要共享的状态,按照共享范围从小到大依次有三种场景:父子组件间共享状态,不同子树上组件间共享状态和不同组件树间共享状态。

  • 父子组件间共享状态:如下图,”父组件”和其子组件”子组件A”、”子组件B”共享状态loading。

    图3 父子组件间共享状态场景
    3

  • 不同子树上组件间共享状态:如下图,祖先组件的左子树上”孙子组件AAA”和右子树上”孙子组件BAA”共享状态loading。

    图4 不同子树上组件间状态共享场景
    4

  • 不同组件树间共享状态:如下图,组件树A内”子组件AA”和组件树B内”孙子组件AAA”共享状态loading。

    图5 不同组件树间共享状态的场景
    5

对于上述三种场景,ArkUI提供了[@State+@Prop]、[@State+@Link]、[@State+@Observed+@ObjectLink]、[@Provide+@Consume]、[AppStorage]、[LocalStorage]六种装饰器组合以解决不同范围内的组件间状态共享。按照共享范围能力从小到大,各装饰器组合的共享范围能力和生命周期如下:

  1. @State+@Prop、@State+@Link、@State+@Observed+@ObjectLink:三者的共享范围为从@State所在的组件开始,到@Prop/@Link/Ob
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值