首页 > 其他分享 >鸿蒙Next状态管理最佳实践

鸿蒙Next状态管理最佳实践

时间:2024-12-16 14:13:21浏览次数:8  
标签:组件 鸿蒙 Color testClass Next 最佳 状态变量 message translateObj

在鸿蒙Next应用开发中,合理的状态管理是确保应用性能和响应性的关键。以下是基于最佳实践的详细阐述,每个实践都包含反例分析和正例改进,并提供了相应的代码示例。

一、使用@ObjectLink代替@Prop减少不必要的深拷贝

(一)问题场景

在父子组件数据传递时,如果子组件不需要改变传递过来的数据,使用@Prop装饰器会带来不必要的深拷贝开销,影响性能。

(二)反例分析

以下代码展示了一个父组件Parent和子组件PropChild之间的数据传递。父组件中有一个@State修饰的testClass数组,包含MyClass的实例。子组件使用@Prop接收testClass

// 反例
@Observed
class MyClass {
  public num: number = 0;
  constructor(num: number) {
    this.num = num;
  }
}

@Component
struct PropChild {
  @Prop testClass: MyClass; // @Prop会深拷贝数据
  build() {
    Text(`PropChild testNum ${this.testClass.num}`)
  }
}

@Entry
@Component
struct Parent {
  @State testClass: MyClass[] = [new MyClass(1)];
  build() {
    Column() {
      Text(`Parent testNum ${this.testClass[0].num}`)
      .onClick(() => {
          this.testClass[0].num += 1;
        })
      // PropChild没有改变@Prop testClass: MyClass的值,但@Prop会深拷贝数据,有性能开销
      PropChild({ testClass: this.testClass[0] })
    }
  }
}

(三)正例改进

PropChild中的@Prop改为@ObjectLink,避免了深拷贝,提高了性能。

// 正例
@Observed
class MyClass {
  public num: number = 0;
  constructor(num: number) {
    this.num = num;
  }
}

@Component
struct PropChild {
  @ObjectLink testClass: MyClass; // @ObjectLink不会深拷贝数据
  build() {
    Text(`PropChild testNum ${this.testClass.num}`)
  }
}

@Entry
@Component
struct Parent {
  @State testClass: MyClass[] = [new MyClass(1)];
  build() {
    Column() {
      Text(`Parent testNum ${this.testClass[0].num}`)
      .onClick(() => {
          this.testClass[0].num += 1;
        })
      // 子组件不需要改变数据时,使用@ObjectLink性能更好
      PropChild({ testClass: this.testClass[0] })
    }
  }
}

二、不使用状态变量强行更新非状态变量关联组件

(一)问题场景

开发者不应通过自定义UI状态变量来更新未被装饰为状态变量的常规变量,因为在ArkUI中,UI更新应由框架自动检测状态变量的更改来实现。

(二)反例分析

MyComponent组件中,realStateArrrealState未被装饰为状态变量,改变它们的值不会触发UI刷新,而通过needsUpdate状态变量来带动它们的更新,这种方式不合理且性能差。

// 反例
@Entry
@Component
struct MyComponent {
  @State needsUpdate: boolean = true;
  realStateArr: Array<number> = [4, 1, 3, 2]; // 未使用状态变量装饰器
  realState: Color = Color.Yellow;
  updateUIArr(param: Array<number>): Array<number> {
    const triggerAGet = this.needsUpdate;
    return param;
  }
  updateUI(param: Color): Color {
    const triggerAGet = this.needsUpdate;
    return param;
  }
  build() {
    Column({ space: 20 }) {
      ForEach(this.updateUIArr(this.realStateArr),
        (item: Array<number>) => {
          Text(`${item}`)
        })
      Text("add item")
      .onClick(() => {
          // 改变realStateArr不会触发UI视图更新
          this.realStateArr.push(this.realStateArr[this.realStateArr.length - 1] + 1);
          // 触发UI视图更新
          this.needsUpdate =!this.needsUpdate;
        })
      Text("chg color")
      .onClick(() => {
          // 改变realState不会触发UI视图更新
          this.realState = this.realState == Color.Yellow? Color.Red : Color.Yellow;
          // 触发UI视图更新
          this.needsUpdate =!this.needsUpdate;
        })
    }.backgroundColor(this.updateUI(this.realState))
    .width(200).height(500)
  }
}

(三)正例改进

realStateArrrealState@State装饰,使其成为状态变量,改变它们的值就能直接触发UI更新。

// 正例
@Entry
@Component
struct CompA {
  @State realStateArr: Array<number> = [4, 1, 3, 2];
  @State realState: Color = Color.Yellow;
  build() {
    Column({ space: 20 }) {
      ForEach(this.realStateArr,
        (item: Array<number>) => {
          Text(`${item}`)
        })
      Text("add item")
      .onClick(() => {
          // 改变realStateArr触发UI视图更新
          this.realStateArr.push(this.realStateArr[this.realStateArr.length - 1] + 1);
        })
      Text("chg color")
      .onClick(() => {
          // 改变realState触发UI视图更新
          this.realState = this.realState == Color.Yellow? Color.Red : Color.Yellow;
        })
    }.backgroundColor(this.realState)
    .width(200).height(500)
  }
}

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

(一)问题场景

将同一状态变量绑定到多个同级组件的属性上,当状态变量改变时,所有关联组件都会刷新,即使它们的变化相同,这可能导致不必要的组件刷新,影响性能。将状态变量绑定到父组件上可以减少需要刷新的组件数,提高性能。

(二)反例分析

Page组件中,translateObjtranslateX属性被多个同级子组件(Title中的ImageTextStackButton)绑定,当translateX变化时,所有这些组件都会刷新。

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

@Component
struct Title {
  @ObjectLink translateObj: Translate;
  build() {
    Row() {
      // 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。
      Image($r('app.media.icon'))
      .width(50)
      .height(50)
      .translate({
          x: this.translateObj.translateX // this.translateObj.translateX绑定在Image和Text组件上
        })
      Text("Title")
      .fontSize(20)
      .translate({
          x: this.translateObj.translateX
        })
    }
  }
}

@Entry
@Component
struct Page {
  @State translateObj: Translate = new Translate();
  build() {
    Column() {
      Title({
        translateObj: this.translateObj
      })
      Stack() {
      }
      .backgroundColor("black")
      .width(200)
      .height(400)
      .translate({
          x: this.translateObj.translateX // this.translateObj.translateX绑定在Stack和Button组件上
        })
      Button("move")
      .translate({
          x: this.translateObj.translateX
        })
      .onClick(() => {
          animateTo({
            duration: 50
          }, () => {
            this.translateObj.translateX = (this.translateObj.translateX + 50) % 150
          })
        })
    }
  }
}

(三)正例改进

将子组件共同的translate属性统一设置在父组件Column上,减少了状态变量关联的组件数。

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

@Component
struct Title {
  build() {
    Row() {
      // 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。
      Image($r('app.media.icon'))
      .width(50)
      .height(50)
      Text("Title")
      .fontSize(20)
    }
  }
}

@Entry
@Component
struct Page1 {
  @State translateObj: Translate = new Translate();
  build() {
    Column() {
      Title()
      Stack() {
      }
      .backgroundColor("black")
      .width(200)
      .height(400)
      Button("move")
      .onClick(() => {
          animateTo({
            duration: 50
          }, () => {
            this.translateObj.translateX = (this.translateObj.translateX + 50) % 150
          })
        })
    }
    .translate({ // 子组件Stack和Button设置了同一个translate属性,统一到Column上设置
        x: this.translateObj.translateX
      })
  }
}

四、合理控制对象类型状态变量关联的组件数量

(一)问题场景

当一个复杂对象被定义为状态变量时,其任何成员属性的变化都会导致关联的所有组件刷新,即使部分组件未使用该改变的属性,这会造成“冗余刷新”,影响性能。

(二)解决方法

合理拆分复杂对象,控制其关联的组件数量,避免不必要的组件刷新。具体可参考相关文章(如文档中提到的精准控制组件的更新范围和状态管理合理使用开发指导)。

五、查询状态变量关联的组件数

(一)操作方法

在应用开发中,可以通过HiDumper查看状态变量关联的组件数,以进行性能优化。具体操作可参考状态变量组件定位工具实践。

六、避免在for、while等循环逻辑中频繁读取状态变量

(一)问题场景

在循环逻辑中频繁读取状态变量会影响性能,因为每次读取都可能触发相关的更新机制。

(二)反例分析

Index组件中,onClick事件的for循环里每次都读取@State message状态变量,这会影响性能。

// 反例
import hilog from '@ohos.hilog';

@Entry
@Component
struct Index {
  @State message: string = '';
  build() {
    Column() {
      Button('点击打印日志')
      .onClick(() => {
          for (let i = 0; i < 10; i++) {
            hilog.info(0x0000, 'TAG', '%{public}s', this.message);
          }
        })
      .width('90%')
      .backgroundColor(Color.Blue)
      .fontColor(Color.White)
      .margin({
          top: 10
        })
    }
    .justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Center)
    .margin({
        top: 15
      })
  }
}

(三)正例改进

在循环外先读取状态变量到临时变量,然后在循环中使用临时变量,减少了对状态变量的读取次数,提高了性能。

// 正例
import hilog from '@ohos.hilog';

@Entry
@Component
struct Index {
  @State message: string = '';
  build() {
    Column() {
      Button('点击打印日志')
      .onClick(() => {
          let logMessage: string = this.message;
          for (let i = 0; i < 10; i++) {
            hilog.info(0x0000, 'TAG', '%{public}s', logMessage);
          }
        })
      .width('90%')
      .backgroundColor(Color.Blue)
      .fontColor(Color.White)
      .margin({
          top: 10
        })
    }
    .justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Center)
    .margin({
        top: 15
      })
  }
}

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

(一)问题场景

直接对状态变量赋值会多次触发ArkUI的查询和渲染行为,因为每次赋值都被视为状态变量的变化,这会影响性能。

(二)反例分析

Index组件的appendMsg方法中直接操作@State message状态变量,多次触发计算函数,增加了ArkUI不必要的查询和渲染,性能较差。

// 反例
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';

@Entry
@Component
struct Index {
  @State message: string = '';
  appendMsg(newMsg: string) {
    // 性能打点
    hiTraceMeter.startTrace('StateVariable', 1);
    this.message += newMsg;
    this.message += ';';
    this.message += '<br/>';
    hiTraceMeter.finishTrace('StateVariable', 1);
  }
  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
      })
  }
}

(三)正例改进

使用临时变量进行数据计算,最后再将计算结果赋值给状态变量,减少了ArkUI不必要的行为,提高了性能。

// 正例
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';

@Entry
@Component
struct Index {
  @State message: string = '';
  appendMsg(newMsg: string) {
    // 性能打点
    hiTraceMeter.startTrace('TemporaryVariable', 2);
    let message = this.message;
    message += newMsg;
    message += ';';
    message += '<br/>';
    this.message = message;
    hiTraceMeter.finishTrace('TemporaryVariable', 2);
  }
  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
      })
  }
}

通过遵循这些状态管理最佳实践,鸿蒙Next开发者能够优化应用性能,提升用户体验,确保应用在各种场景下都能高效运行。

标签:组件,鸿蒙,Color,testClass,Next,最佳,状态变量,message,translateObj
From: https://www.cnblogs.com/freerain/p/18609962

相关文章

  • 鸿蒙Next MVVM思想总结
    一、MVVM模式概述在鸿蒙Next的ArkUI框架中,MVVM(Model-View-ViewModel)模式是一种重要的架构模式,用于管理应用程序中的数据和UI之间的交互。MVVM模式通过将数据和视图分离,使得应用程序的开发更加高效、可维护和可测试。(一)MVVM模式的组成部分Model层:存储数据和相关逻辑的模型,表示......
  • 掌控复杂跨境项目?在线文档是你的最佳伙伴
    打破国界的协作:在线文档如何重塑跨境团队效率在如今复杂的商业环境中,跨境团队协作已成为许多企业的常态。然而,如何在文化、时差和语言差异中实现无缝对接,成为一大难题。在线文档协作正在以其独特的优势,为企业开启新的高效协作模式。全球化带来的协作挑战跨境团队面临的挑战可以......
  • ARMS 用户体验监控正式发布原生鸿蒙应用 SDK
    作者:杨兰馨(楠瑆)背景2024年10月22日,华为正式发布了原生鸿蒙操作系统(HarmonyOSNEXT)。原生鸿蒙实现了系统底座全部自研,系统的流畅度、性能、安全特性等方面显著提升,也实现了操作系统的自主可控。目前,已有超过15000个鸿蒙原生应用和元服务上架,为了进一步优化用户的使用体验,......
  • HarmonyOS Next 入门实战 - 文字转拼音,文字转语音
    文字转拼音安装pinyin4js三方库ohpminstall@ohos/pinyin4jspinyin4js提供了以下接口:●文字转拼音(带声调和不带声调)●文字转拼音首字母●简体繁体互转letrawText="风急天高猿萧哀,渚清沙白鸟飞回;"letpinyin1:string=pinyin4js.convertToPinyinString(rawT......
  • HarmonyOS Next分布式智能家居控制系统实战
    本文旨在深入探讨基于华为鸿蒙HarmonyOSNext系统(截止目前API12)构建分布式智能家居控制系统的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。一、项......
  • HarmonyOS Next 设备适配与生态拓展
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)在设备适配与生态拓展方面的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。一、设备适配......
  • HarmonyOS Next 应用性能优化实战
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)中应用性能优化的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。一、性能评估指标与工具......
  • Go单体服务开发最佳实践
     转: https://www.cnblogs.com/kevinwan/p/16193504.html------------------------------------------------------------------------------------------单体最佳实践的由来对于很多初创公司来说,业务的早期我们更应该关注于业务价值的交付,并且此时用户体量也很小,QPS 也非......
  • HarmonyOS Next 中的 HAP、HAR、HSP 区别
    HarmonyOSNext中的HAP、HAR、HSP区别想要更加合理的开发一个企业级别的Harmony应用,那么就不得不提其中的HAP、HAR、HSP了。前言对于普通的用户来说,可能一个普通的应用就等于一个安装文件如安卓下的APK。但是对于Harmony应用开发工程师来讲,一个应用包含的内容仅仅不止......
  • 带你实现 HarmonyOS Next 微信聊天 02
    带你实现HarmonyOSNext微信聊天02接上一篇前言代码会统一放在码云上案例目标这个是安卓手机上的真正的微信聊天界面功能效果实际效果案例功能上一篇,已经实现了以下功能页面沉浸式聊天内容滚动输入框状态切换聊天信息框宽度自适应输入法避让canvas......