HarmonyOS 状态管理(V1、V2)
本文对比了鸿蒙系统中的MVVM架构与MVC、MVP架构,重点分析了V1和V2装饰器的差异。V1装饰器存在嵌套属性监听不足、性能浪费等问题,而V2通过@ObservedV2+@Trace等机制实现了深度监听、计算属性和更清晰的状态流向。虽然V2在性能、易用性上有显著提升,但仍存在与V1不兼容、动画支持不足、序列化异常等缺陷。文章建议开发者根据项目需求选择合适的装饰器版本,并指出V2的不足将逐步完善。
一、开发架构
相比于传统的网络架构,如:MVC,MVP的开发架构,其底层的数据源都是单向的,其Model层皆用于存储数据结构、工具类或是封装一些通用的网络请求,View层都使用于用于UI视图的渲染和展示。
1.1 MVC
两种开发架构的区别是在于在MVC的开发架构中,Controller层作为连接Model层和View层的桥梁,用于接受用户的输入,根据输入更新Model,并决定将那个View呈现给用户,并没有做到UI层与Model层的解耦,可能会导致数据混乱,出现Controller臃肿的现象,导致胖控制器的现象。
1.2 MVP
那么,MVP的开发架构,通过引入了Presenter,不直接与Model层进行交互,Presenter层作为View和Model间的协调者,从View层接受用户事件,向Model层请求数据执行操作,通过view层的接口命令更新界面
1.3 MVVM
MVVM作为鸿蒙生态采用的首选架构,则采用了MVVM的开发范式通过MVVM架构+状态变量通过数据流的双向绑定实现状态改变驱动视图更新。这就意味着,ViewModel层作为状态变化的桥梁,在ViewModel层处理业务的逻辑,View数据层(Model层)通过状态变量来装饰数据,当数据发生变化,框架会驱动视图层(View层)的更新。
这也就引出了装饰器,那么什么是装饰器,装饰器又有哪些呢?它们的底层实现又是怎样的呢?这,就引出了鸿蒙的状态管理。
二、鸿蒙中的装饰器
在鸿蒙中,其装饰器的实现采用了观察者模式,也叫发布-订阅模式。以声明式范式UI进行开发。在鸿蒙中,装饰器的版本分为V1、V2,其在使用上,也会有一些区别。
2.1 解决的问题
V1升级到V2核心原因是解决了V1装饰器在数据观察能力、性能、易用性等方面的限制,让开发者能更高效、灵活的实现数据驱动UI更新
1️⃣ 解决了V1装饰器深度观测与嵌套属性变化监听,嵌套对象的属性变化无法触发UI刷新,V2数据本身可观察,通过@Observed+@Trace直接观测嵌套对象属性变化
2️⃣ V1装饰器没有计算属性,UI表达式使用同一表达式会重复计算,浪费CPU,性能浪费,V2装饰器提供@Computed属性,表达式只会在依赖发生变化时计算1次,多次绑定不会发生重复执行,提升性能
3️⃣ V1的@Watch 只能监听变量本身及第一层属性,无法监听嵌套属性,且拿不到变化前的值。但是, V2的@Monitor 可深度监听任意层级属性变化,能获取变化前后的值,支持一次监听多个变量,便于复杂业务逻辑处理
4️⃣ V1的@Link 等装饰器让父子组件双向同步很方便,但状态流向不清晰,复杂项目中易出现维护难题。V2 用 @Param 实现父→子单向传递, @Event 实现子→父通知,双向同步由事件驱动,状态流向明确,代码结构更清晰,利于组件化与复用。
5️⃣ V1 的 AppStorage 、 PersistentStorage 与框架耦合强,使用受限。V2 的改进 AppStorageV2 、 PersistenceV2 更灵活, PersistenceV2 可独立持久化,不依赖 AppStorage ; Environment 可直接调用 Ability 接口获取系统参数,解耦更彻底
2.2 V1装饰器
在鸿蒙的V1装饰器中,使用代理观察者,创建状态变量时会生成数据代理,只能监听第一层属性变化。对于自定义组件,要使用@Component进行装饰,对于变量的装饰器的选择,如果,一个基本数据类型的变量,通过交互想让视图进行更新渲染,则可以使用@State装饰器,此装饰器在使用时必须得对变量进行初始化操作。
当我们想进行父子组件通信时,可以使用@Prop和@State组合实现,此方式用于父子组件的单向通信,在使用时,@State装饰的变量必须进行赋值,@Prop装饰的变量,可以选择赋值或者不进行初始化,在使用时,父子组件的数据流是单向传递的,父组件的修改会引起子组件UI的变更,反之,不会引起UI的刷新。装饰器的底层,则是通过深拷贝,以值传递的方式,进行了数据的改变,频繁进行此操作,性能可能会比较差。
当我们想进行父子组件的双向通信时,可以使用@Link和@State组合实现,这种方式,对于子组件使用@Link装饰器修饰的变量,不能进行初始化操作,其底层通过浅拷贝,引用传递的方式,对对象本身的值进行修改,由于改变对象数据时是修改的指针的指向,因此,这种方式的内存开销会相对更小。
对于跨组件传递,则可以使用@Provide和@Consume装饰器去协同搭配。父组件提供要消费的事件,子组件通过父组件提供的数据去消费事件。
对于嵌套的复杂数据类型,@Observed/@ObjectLink 可观察第一层,嵌套属性需自定义组件层层传递,代码复杂。
在V1装饰器对状态变量的监听,使用@Watch 监听变量本身及第一层属性,不能获取变化前值。
2.3 V2装饰器
在鸿蒙的V2装饰器中,数据本身可观察,无需代理,支持深度观测和属性级更新。 对于自定义组件,要使用@ComponentV2进行装饰,对于变量的装饰器的选择,使用@Local装饰器来进行修饰,此装饰器在装饰复杂数据类型时,需要对对象整体来进行一个赋值操作。
在对需要深度观察嵌套对象时可以使用ObservedV2+Trace实现类属性的深层监听,如:复杂数据模型或是需要监听对象内部任意层级属性变化并出发UI更新的情况
如果需要精准监听状态变化的时候,可以使用@Monitor和@Trace深层监听,在同一事件发生多次变化时,仅以最终结果判断是否触发,可以避免重复计算与渲染,如:大型列表或图表数据计算或是对个状态派生出的新状态且保持高性能的场合
在父子组件需要灵活传递并同步数据或是只管控制数据楼的流向和同步机制时,可以使用@Param和@Event手动实现双向同步或是使用@Param实现同步,复杂数据类型可以直接使用@Param装饰器
当需要跨Ability或是跨页面的全局状态共享时,可以使用AppStorageV2全局单例存储,可以跨UIAbility实例共享状态,或是使用PersistentV2持久化存储选定状态,重启后仍可恢复,如:多Ability共享用户登录态、主题、语言…或是持久化保存用户操作状态的应用
对于嵌套的复杂数据类型,@ObservedV2 + @Trace 可直接深度观察嵌套对象属性,无需额外组件。
在V2装饰器对状态变量的监听,则使用@Watch 监听变量本身及第一层属性,不能获取变化前值。@Monitor 可深度监听,能获取变化前后值,且多次变化只以最终结果触发。同时,提供 @Computed ,计算属性只计算一次,避免重复运算。
2.4 V2装饰器的缺点
与V1不兼容:V2装饰器(如 @Local 、 Param 等)不能在V1组件( @Component 装饰的struct)中使用
混用规则复杂且严格:V1和V2组件在传递变量时,数据类型和装饰器有严格限制
动画支持不完善:在状态管理V2中使用 animateTo 实现动画时,可能出现动画效果异常。在执行动画前如果存在额外的状态修改,这些修改可能不会生效,导致动画起始状态不符合预期,解决动画失效,可以将属性的改变放在animationTo属性动画的回调中,或者设置定时器去分批执行操作,或者RequestAnimationFrame中设置定时器在动画的最后一帧去设置动画
部分高级组件和主题API缺失(低版本):在低版本API(API 18以前)中,状态管理V2未提供部分高级UI组件(如 DownloadFileButton 、 ProgressButton 、 SegmentButton )和自定义局部页面主题的能力( WithTheme 组件)
组件复用能力受限(低版本):在低版本API中,V2未提供 @Reusable 装饰器,无法复用自定义组件。这意味着从组件树上移除的大量组件无法放入复用缓存,重新创建时会消耗更多时间。
序列化/反序列化问题: @ObservedV2 与 @Trace 装饰的对象,其属性名在底层会被添加 __ob_ 前缀。这会导致使用 JSON.stringify 等标准方法进行序列化时出现异常(key带前缀),反序列化也会失败。
持久化限制较多: PersistenceV2 (V2的持久化方案)存在较多限制,不支持 collections.Set 、 collections.Map 等类型。持久化的数据必须是class对象,不支持容器类型、built-in构造对象(pixel map)和基本类型
三、总结
鸿蒙开发架构遵循先进的MVVM范式,核心在于一套基于观察者模式的数据驱动状态管理机制。该机制经历了从V1到V2的重大升级,旨在解决V1版本在深度观测、性能和状态流向上的核心痛点。V2版本通过@ObservedV2与@Trace实现了对嵌套对象的深层属性监听,引入@Computed计算属性避免重复渲染以提升性能,并提供了功能更强大的@Monitor深度监听器。同时,它通过@Param和@Event组合,废弃了V1中流向模糊的@Link,规范了父子组件间清晰的单向数据流。尽管V2带来了显著提升,但也引入了与V1不兼容、JSON.stringify序列化异常以及动画支持不完善等新的挑战,要求开发者在享受其强大功能的同时,注意这些实践中的问题。
更多推荐



所有评论(0)