【HarmonyOS Next】鸿蒙状态管理V2装饰器详解 原创
头像 GeorgeGcs 2025-06-30 22:50:37    发布
2693 浏览 2 点赞 0 收藏

## 【HarmonyOS Next】鸿蒙状态管理V2装饰器详解


## 一、为什么需要V2状态管理装饰器?

首先我们需要了解什么是**状态管理**?在鸿蒙应用开发中,状态管理指的是,管理数据变化去刷新UI的整个过程。


举个例子,比如在界面中标题文本的动态刷新,从A刷新成B,这个文本的刷新过程,其实就是个状态的变化过程。整个过程的处理可以称之为状态管理。


鸿蒙使用的ArkUI框架进行渲染,配套的ArkTS是**声明式**编程,只需要关心数据的变化,数据变UI就相应的需要去更新。和传统的**命令式**编程相比,省去了寻找对应UI组件,去填充改变刷新,再让UI进行刷新的过程。

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0a5bed5f1e284e71be9e4b52202ec154.png)


为了实现上述的UI动态渲染效果。鸿蒙提供了**状态装饰器**的概念工具,来实现数据到UI的便捷更新同步。


![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ff3ba23637734d94b952c19abb1f929b.png)


V1状态装饰器于此产生:围绕着@State这个数据监听开关,配套的装饰器来一起实现刷新行为,因为UI界面分为组件,界面。需要维护子母关系。所以就用到了@Prop 单数据流动,@Link 双数据流动,@Provide/@Consume 跨层级传递数据,@Observed和@ObjectLink 监听实现多层级嵌套对象的更新。


自从api7开始,一直到api10。V1的实际使用中,开发人员发现@Observed和@ObjectLink 监听实现多层级嵌套对象的更新的方案,太过于臃肿。


当需要监听处理更新的多层级对象是七八层,就需要配套创建七八层的ObjectLink,代码太过于冗余。


**代码举例如下:**

需要进行嵌套数据更新的层级,要抽离ItemView,在其中进行ObjectLink的处理。由此逻辑推断,如果我的数据对象有十层,每层都有数据需要更新的业务逻辑,(极限情况),代码冗余十分致命。


```dart

import { util } from '@kit.ArkTS';


/**

* 三级数据结构

*/

@Observed // 每一级数据结构都需要用Observed修饰

class GrandsonInfo {

 content: string = "";


}


/**

* 二级数据结构

*/

@Observed // 每一级数据结构都需要用Observed修饰

class ChildInfo {

 index: number;

 grandsonInfo: GrandsonInfo;


 constructor(index: number, content: string) {

   this.index = index;

   this.grandsonInfo = new GrandsonInfo();

   this.grandsonInfo.content = content;

 }

}


/**

* 一级数据结构

*/

@Observed // 每一级数据结构都需要用Observed修饰

class ItemInfo {

 key: string = util.generateRandomUUID(true);

 name: string;

 icon: Resource;

 childInfo: ChildInfo;

 select: boolean;


 constructor(name: string, icon: Resource, index: number, content: string) {

   this.name = name;

   this.icon = icon;

   this.childInfo = new ChildInfo(index, content);

   this.select = false;

 }

}


/**

* 多层嵌套刷新渲染

*/

@Entry

@Component

struct ObservedPage {

 private TAG: string = "ObservedPage";


 @State mListData: Array<ItemInfo> = [];


 aboutToAppear(): void {

   this.mListData.push(new ItemInfo('游戏', $r("app.media.iconA"), 1, "鹅厂1"));

   this.mListData.push(new ItemInfo('游戏', $r("app.media.iconB"), 2, "鹅厂2"));

   this.mListData.push(new ItemInfo('游戏', $r("app.media.iconA"), 3, "鹅厂3"));

   this.mListData.push(new ItemInfo('游戏', $r("app.media.iconB"), 4, "鹅厂4"));

   this.mListData.push(new ItemInfo('游戏', $r("app.media.iconA"), 5, "鹅厂5"));

   this.mListData.push(new ItemInfo('游戏', $r("app.media.iconB"), 6, "鹅厂6"));

 }


 build() {

   List() {

     ForEach(this.mListData, (item: ItemInfo, index: number) => {

       ListItem() {

         // ListItem包裹的ItemView需要抽离成Component组件的形态,参数通过属性赋值传递,即:大括号包裹中,属性值key value形式赋值

         ItemView({

           item: item,

           index: index

         })

       }

     }, (item: ItemInfo) => JSON.stringify(item))

   }

   .width("100%")

   .height("100%")

   .padding({ left: px2vp(60), right: px2vp(60) })

 }

}

@Component

struct ItemView {


 private TAG: string = "ItemView";


 @Prop index: number = 0;

 // 列表数据的单个item对象数据,需要使用ObjectLink修饰监听,用于将数据变化传递给外部父组件的mListData

 @ObjectLink item: ItemInfo


 build() {

   Row() {

     Image(this.item.icon)

       .width(px2vp(200))

       .height(px2vp(200))


     Text(this.item.name + "(" + this.item.childInfo.index + ")" + " [ " + this.item.childInfo.grandsonInfo.content + " ] ")

       .fontSize(px2fp(52))


     Blank()


     if(this.isLog(this.item, this.index)){

       if(this.item.select){

         Image($r("app.media.icon_check"))

           .size({

             width: px2vp(72),

             height: px2vp(72)

           })

       }

     }

   }

   .width('100%')

   .justifyContent(FlexAlign.Start)

   .onClick(()=>{

     this.item.select = !this.item.select;

     if(this.item.select){

       // 使用很方便,只需要直接改变item数据的任意层级属性值,变化就会同步刷新

       this.item.childInfo.index = 666;

       this.item.childInfo.grandsonInfo.content = "鹅厂23333"

     }else{

       this.item.childInfo.index = this.index;

       this.item.childInfo.grandsonInfo.content = "鹅厂" + this.index;

     }

     console.log(this.TAG, " ItemView onClick: " + this.index + " item.select: " + this.item.select);

   })

 }


 private isLog(item: ItemInfo, index: number){

   console.log(this.TAG, " ItemView isLog index: " + index + " item.select: " + item.select);

   return true;

 }

}


```

***正因为以上的缺陷,V2状态管理装饰器由此诞生。但是目前开发进程还有堵塞点,官方建议慎重使用V2。***


## 二、V2状态管理装饰器怎么用?

综上所述,我们知道解决多层嵌套对象刷新的痛点,是V2的主要任务。


其中@ObservedV2和@Trace,代替了@Observed和@ObjectLink。


相对于V1的监听原理,使用代理模式的监听数据变化。V2更加彻底且解耦,直接在数据本身进行观察监听。


![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ab19fbc8ef2845acb736c6f6f7def686.png)

 **代码举例如下:**

 首先要给需要刷新的多层对象定义类,添加@ObservedV2修饰。这个和V1区别不大。之后是使用@Trace直接修饰在需要监听的属性之前,代表该属性需要观察。此时我们就需要关心这个属性,在对象多少层了。

```dart

@ObservedV2

class Father {

 @Trace name: string = "Tom";

}

class Son extends Father {

}

@Entry

@ComponentV2

struct Index {

 son: Son = new Son();


 build() {

   Column() {

     // 当点击改变name时,Text组件会刷新

     Text(`${this.son.name}`)

       .onClick(() => {

         this.son.name = "Jack";

       })

   }

 }

}

@ObservedV2

class M

anager {

 @Trace static count: number = 1;

}

@Entry

@ComponentV2

struct Index {

 build() {

   Column() {

     // 当点击改变count时,Text组件会刷新

     Text(`${Manager.count}`)

       .onClick(() => {

         Manager.count++;

       })

   }

 }

}

```


**@ComponentV2装饰器:自定义组件**

**@Local 组件内部状态**

@Local可理解为v1版本的@State,当被@Local装饰的变量变化时,会刷新使用该变量的组件。


**@Param 组件外部输入**

@Param可理解为v1版本的@Prop,但不同的是,如果单一使用@Param,则不可修改子组件当前的数据用于改变UI展示,修改时会报错,若想修改,则需要再增加一个@Once 装饰器。


**@Once 初始化同步一次**

增加@once 装饰器后可修改 msg 的值,会触发当前组件UI更新,但数据不会同步至父组件。需要注意:仅初始化时同步数据源一次,之后不再继续同步变化的场景。比如父组件 @Local装饰的变量存在初始值,传递给@Once装饰的变量后,再次修改@Local变量的值,子组件的值不会发生变化。


**@Require**

@Param默认需要设置初始值,若不想设置初始值,需要增加 @Require 装饰器。



**@Event 规范组件输出**

实现子组件向父组件要求更新@Param装饰变量的能力(数据双向绑定),父组件传递至子组件的数据,若子组件想修改数据并同步至父组件,则需要使用该装饰器实现。@Event装饰器只可修饰回调方法,装饰string或number等类型不报错,但也无法发挥其装饰器功能。


使用@Event和@Param实现数据双向绑定。

## 三、V2状态管理装饰器的优点和不足


**V2的优点**

1. 具备深度观察、属性级更新。

2. 组件中明确状态变量的输入与输出,利于组件化

3. 状态变量能独立于UI存在,同一个数据被多个视图代理时,在其中一个视图的更改会通知其他视图更新


**V2的不足**

1. 复杂对象时V1的@State能够观察复杂对象的第一层属性变化,但V2的@Local无法观察对象内部变化。为了解决这个问题,需要在类上添加@ObservedV2,并在需要观察的属性上添加@Trace。这样,框架就能追踪对象内部的属性变化。相对于V1稍微麻烦些。

2. animateTo暂不支持直接在状态管理V2中使用。



[点击跳转官方V1迁移V2的配套指导。](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V14/arkts-v1-v2-migration-V14)

©本站发布的所有内容,包括但不限于文字、图片、音频、视频、图表、标志、标识、广告、商标、商号、域名、软件、程序等,除特别标明外,均来源于网络或用户投稿,版权归原作者或原出处所有。我们致力于保护原作者版权,若涉及版权问题,请及时联系我们进行处理。
分类
HarmonyOS

暂无评论数据

发布

头像

GeorgeGcs

HarmonyOS认证学习资源创作专家,华为HDE专家,鸿蒙讲师,作者。目前任职鸿蒙应用架构师。 历经腾讯,宝马,研究所,金融。 待过私企,外企,央企。 深耕大应用开发领域十年。 OpenHarmony,HarmonyOS,Flutter,H5,Android,IOS。

125

帖子

1

提问

506

粉丝

关注
热门推荐
地址:北京市朝阳区北三环东路三元桥曙光西里甲1号第三置业A座1508室 商务内容合作QQ:2291221 电话:13391790444或(010)62178877
版权所有:电脑商情信息服务集团 北京赢邦策略咨询有限责任公司
声明:本媒体部分图片、文章来源于网络,版权归原作者所有,我司致力于保护作者版权,如有侵权,请与我司联系删除
京ICP备:2022009079号-2
京公网安备:11010502051901号
ICP证:京B2-20230255