鸿蒙Next状态管理最佳实践

news/2024/12/16 13:56:25/文章来源:https://www.cnblogs.com/freerain/p/18609962

在鸿蒙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开发者能够优化应用性能,提升用户体验,确保应用在各种场景下都能高效运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/853767.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推荐一款强大的开源物联网 Web 组态软件

前言 快速发展的物联网(IoT)领域,设备管理和监控的需求日益增长。为了满足这一需求并提供更高效的解决方案。 向大家推荐一款强大的开源物联网Web组态软件。这款软件不仅具备灵活的可视化配置功能,还提供了丰富的工具和接口,轻松实现设备集成、数据监控和远程管理。 项目介…

Win10 wsl 迁移到 D 盘

wsl 关闭wsl --shutdown查看系统wsl --list导出 tar 文件wsl --export Ubuntu-24.04 D:/export.tar删除C盘的虚拟机wsl --unregister Ubuntu-24.04导入新的虚拟机wsl --import Ubuntu-24.04 D:\wsl\ D:\export.tar --version 2查看状态直面挑战,躬身入局

PHP源码加密之php-beast

1、简介 php-beast可以对PHP源码文件进行加密,加密后的文件也可以正常的访问。2、安装php-beast# 下载php-beast wget https://github.com/liexusong/php-beast/archive/master.zip# 解压下载的文件 unzip master.zip# 进入解压后的目录 cd php-beast-master# ‌生成配置文件 …

ABB机器人3HAC17332-1电机维修攻略分享

ABB作为工业机器人制造商,其伺服电机是机器人执行精确动作的核心部件。伺服电机负责将电能转化为机械能,驱动机器人的关节和臂部运动。一旦伺服电机出现故障,将直接影响机器人的运行精度和稳定性。一、ABB机器人维修前准备在进行ABB机器人伺服电机维修之前,需要做好以下准备…

大话《权限设计》全篇,领略不同设计模式的魅力

说明该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发)。该系统文章,我会尽量说的非常详细,做到不管新手、老手都能看懂。说明:OverallAuth2.0 是一个简单、易懂、功能强大的权限+可视化流程管理系统。 友情提醒:本篇文章是属于系列文章,…

LLM应用实战-财经新闻自动聚合

开发了一款新闻资讯的自动聚合的工具,基于crawl4ai框架实现。1. 背景 这段时间项目比较忙,所以本qiang~有些耽误了学习,不过也算是百忙之中,抽取时间来支撑一个读者的需求,即爬取一些财经网站的新闻并自动聚合。 该读者看了之前的《AI资讯的自动聚合及报告生成》文章后,…

减少延迟时间的方法

减少延迟时间的方法 ‍ ​​ ‍一、回顾 重点:延迟时间就是磁头在某一磁道上开转的时间 盘片一直在旋转个不停 机械硬盘的数据读取以一个扇区为单位 物理上相邻的扇区并不能连续读数据,因为磁头读入一个扇区的数据后还需要一小段时间来处理,并不能紧接着读取相邻的扇区的数据…

Win电脑端有什么好用的备忘录便签推荐?

一、sticky notes 中文名叫便笺,就是Windows电脑系统自带的便笺,打开即可直接使用,无需安装。 它是一个一个彩色便利贴形式展现的,可以记录文字、添加图片,适合记录一些简单的信息。 不支持一直悬挂在电脑桌面上显示,也不支持设置提醒时间,想要同步到手机端使用,也有点…

设备的分配与回收

设备的分配与回收 ‍ ​​ ‍一、设备分配时的考虑因素 ​​ (一)设备的固有属性独占设备:一个时段只能分配给一个进程(如打印机) 共享设备:可同时分配给多个进程使用(如磁盘),各进程往往是宏观上同时共享使用设备,而微观上交替使用。 虚拟设备:采用 SPOOLing 技术将…

GitHub项目迁移到GitLab

GitHub项目迁移到GitLab 1.克隆GitHub项目到本地 [root@gitclient ~]# mkdir gitrepos [root@gitclient ~]# cd gitrepos [root@gitclient gitrepos]# git init . hint: Using master as the name for the initial branch. This default branch name hint: is subject to chang…

极狐GitLab 正式发布安全补丁版本 17.6.2、17.5.4、 17.4.6

本分分享极狐GitLab 补丁版本 17.6.2, 17.5.4, 17.4.6 的详细内容。这几个版本包含重要的缺陷和安全修复代码,我们强烈建议所有私有化部署用户应该立即升级到上述的某一个版本。对于极狐GitLab SaaS,技术团队已经进行了升级,无需用户采取任何措施。 参考资料GitLab 专业升级…

IO应用程序接口设备驱动程序接口

IO应用程序接口&设备驱动程序接口 ‍ ​​ ‍一、输入/输出应用程序接口 背景:在设备独立软件层向上提供各种各样的输入/输出应用程序接口的原因是: 用户层的应用程序无法用一个统一的系统调用接囗来完成所有类型设备的 I/O ​​ ‍ 三种输入/输出应用程序接口:字符设备…