首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

鸿蒙Next状态管理最佳实践

  • 24-12-16 16:08
  • 2832
  • 27685
juejin.cn

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

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

(一)问题场景

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

(二)反例分析

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

typescript
代码解读
复制代码
// 反例 @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,避免了深拷贝,提高了性能。

typescript
代码解读
复制代码
// 正例 @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组件中,realStateArr和realState未被装饰为状态变量,改变它们的值不会触发UI刷新,而通过needsUpdate状态变量来带动它们的更新,这种方式不合理且性能差。

typescript
代码解读
复制代码
// 反例 @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) } }

(三)正例改进

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

typescript
代码解读
复制代码
// 正例 @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组件中,translateObj的translateX属性被多个同级子组件(Title中的Image和Text、Stack、Button)绑定,当translateX变化时,所有这些组件都会刷新。

typescript
代码解读
复制代码
// 反例 @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上,减少了状态变量关联的组件数。

typescript
代码解读
复制代码
// 正例 @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状态变量,这会影响性能。

typescript
代码解读
复制代码
// 反例 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 }) } }

(三)正例改进

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

typescript
代码解读
复制代码
// 正例 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不必要的查询和渲染,性能较差。

typescript
代码解读
复制代码
// 反例 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 += '
'
;
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不必要的行为,提高了性能。

typescript
代码解读
复制代码
// 正例 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 += '
'
;
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开发者能够优化应用性能,提升用户体验,确保应用在各种场景下都能高效运行。

注:本文转载自juejin.cn的Freerain9的文章"https://juejin.cn/post/7448818246352044059"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

104
前端
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top