鸿蒙Next状态管理优秀实践

embedded/2024/12/20 13:57:39/

在鸿蒙Next应用开发中,高效的状态管理对于提升应用性能至关重要。本文将介绍一些状态管理的优秀实践,帮助开发者避免常见的低效开发场景,提升应用质量。

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

问题描述

在父子组件数值传递中,若子组件不改变状态变量值,使用@Prop装饰状态变量会增加组件创建耗时。

反例

@Observed
class ClassA {public c: number = 0;constructor(c: number ) {this.c = c;}
}
@Component
struct PropChild {@Prop testNum: ClassA; // @Prop装饰状态变量会深拷贝build() {Text(`PropChild testNum ${this.testNum.c}`)}
}
@Entry
@Component
struct Parent {@State testNum: ClassA[] = [new ClassA(1)];build() {Column() {Text(`Parent testNum ${this.testNum[0].c}`).onClick(() => {this.testNum[0].c += 1;})// PropChild没有改变@Prop testNum: ClassA的值,所以这时最优的选择是使用@ObjectLinkPropChild({ testNum: this.testNum[0] })}}
}

正例

@Observed
class ClassA {public c: number = 0;constructor(c: number ) {this.c = c;}
}
@Component
struct PropChild {@ObjectLink testNum: ClassA; // @ObjectLink装饰状态变量不会深拷贝build() {Text(`PropChild testNum ${this.testNum.c}`)}
}
@Entry
@Component
struct Parent {@State testNum: ClassA[] = [new ClassA(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] })}}
}

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

问题描述

开发者自定义UI状态变量来控制非状态变量关联组件更新,这种方式不合理且性能差。

反例

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

正例

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

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

问题描述

同一个状态变量绑定多个同级组件属性,状态变量改变时会导致所有关联组件一起刷新,即使变化相同也会造成不必要刷新,影响性能。

反例

@Observed
class Translate {translateX: number = 20;
}
@Component
struct Title {@ObjectLink translateObj: Translate;build() {Row() {Image($r('app.media.icon')).width(50).height(50).translate({x: this.translateObj.translateX // this.translateObj.translateX used in two component both in Row})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 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')).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({ // the component in Column shares the same property translatex: this.translateObj.translateX})}
}

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

将复杂对象定义为状态变量时,其某个成员属性变化会导致所有关联组件刷新,即使组件未直接使用该属性。建议合理拆分复杂对象,控制关联组件数量。

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

可通过HiDumper查看状态变量关联的组件数进行性能优化,具体参考状态变量组件定位工具实践。

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

反例

@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})}
}

正例

@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})}
}

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

反例

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})}
}

正例

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.ppmy.cn/embedded/147292.html

相关文章

Visio——同一个工程导出的PDF文件大小不一样的原因分析

现象 在不同电脑&#xff0c;导出来的PDF文件大小不一样。 原因分析 文件小的未将字体嵌入&#xff0c;文件大的已经将字体嵌入了。

【数据集】玻璃门窗缺陷检测数据集3085张5类YIOLO+VOC格式

数据集格式&#xff1a;VOC格式YOLO格式 压缩包内含&#xff1a;3个文件夹&#xff0c;分别存储图片、xml、txt文件 JPEGImages文件夹中jpg图片总计&#xff1a;3085 Annotations文件夹中xml文件总计&#xff1a;3085 labels文件夹中txt文件总计&#xff1a;3085 标签种类数&am…

aac怎么转为mp3?操作起来很简单的几种aac转mp3的方法

aac怎么转为mp3&#xff1f;aac格式的优势主要体现在音质和压缩效率&#xff0c;尤其是在较低比特率下&#xff0c;能够实现更清晰的音质&#xff0c;这也是为何许多现代设备和应用偏爱aac格式的原因之一。特别是在手机、平板以及智能音响等设备中&#xff0c;aac文件几乎可以无…

yolov目标检测的图片onnx输入尺寸及预处理

参考 (github.com) 当你使用不同的图像尺寸&#xff08;例如1280&#xff09;进行预测时&#xff0c;YOLOv8会自动对输入图像进行适当的预处理以适配模型。这通常包括缩放和填充操作&#xff0c;确保图像不会发生畸变&#xff0c;同时保持原始宽高比。 对于使用OpenCV进行预处…

数据结构:双向带头循环链表的增删查改

先构建一个结构体&#xff0c;对其链表进行初始化&#xff0c;尾插&#xff0c;尾删&#xff0c;头插&#xff0c;头删&#xff0c;查找&#xff0c;修改&#xff0c;中间插入&#xff0c;删除等相关操作。 List.h #pragma once #include <stdio.h> #include <stdli…

SSM 寝室管理系统:为住宿生活保驾护航

摘 要 寝室管理设计是高校为学生提供第二课堂&#xff0c;而我们所在学院多采用半手工管理学生寝室的方式&#xff0c;所以有必要开发寝室管理系统来对进行数字化管理。既可减轻学院宿舍长工作压力&#xff0c;比较系统地对宿舍通告、卫生上的各项服务和信息进行管理&#xff…

《Vue3实战教程》5:响应式基础

如果您有疑问&#xff0c;请观看视频教程《Vue3实战教程》 响应式基础​ API 参考 本页和后面很多页面中都分别包含了选项式 API 和组合式 API 的示例代码。现在你选择的是 组合式 API。你可以使用左侧侧边栏顶部的“API 风格偏好”开关在 API 风格之间切换。 声明响应式状态…

计算机组成原理课后习题答案:第九章

第九章控制单元功能 9.1设CPU内有这些部件:PC、IR、MAR、MDR、AC、CU。 (1)写出取指周期的全部微操作。 (2)写出减法指令“SUB X”、取数指令“LDA X”、存数指令“STA X”(X均为主存地址)在执行阶段所需全部微操作。 (3)当上述指令为间接寻址时,写出执行这些指令所需的全部…