DEV Community

Freerain
Freerain

Posted on

鸿蒙Next MVVM思想总结

一、MVVM模式概述

在鸿蒙Next的ArkUI框架中,MVVM(Model-View-ViewModel)模式是一种重要的架构模式,用于管理应用程序中的数据和UI之间的交互。MVVM模式通过将数据和视图分离,使得应用程序的开发更加高效、可维护和可测试。

(一)MVVM模式的组成部分

  1. Model层:存储数据和相关逻辑的模型,表示组件或其他相关业务逻辑之间传输的数据,是对原始数据的进一步处理。
  2. View层:在ArkUI中通常是@Component装饰组件渲染的UI,负责展示数据给用户。
  3. ViewModel层:在ArkUI中,ViewModel是存储在自定义组件的状态变量、LocalStorage和AppStorage中的数据,起到数据与视图绑定的作用,负责处理数据的获取、更新和通知视图的变化。

(二)MVVM模式的工作原理

  1. 自定义组件通过执行其build()方法或者@Builder装饰的方法来渲染UI,即ViewModel可以渲染View。
  2. View可以通过相应event handler来改变ViewModel,即事件驱动ViewModel的改变,另外ViewModel提供了@Watch回调方法用于监听状态数据的改变。
  3. 在ViewModel被改变时,需要同步回Model层,以保证ViewModel和Model的一致性,即应用自身数据的一致性。

(三)ViewModel结构设计的目的

ViewModel结构设计应始终为了适配自定义组件的构建和更新,将Model和ViewModel分开的原因在于,目前很多关于UI构造和更新的问题,都是由于ViewModel的设计没有很好地支持自定义组件的渲染,或者试图让自定义组件强行适配Model层,而中间没有用ViewModel来进行分离。例如,直接将SQL数据库中的数据读入内存这种数据模型不能很好地直接适配自定义组件的渲染,所以在应用程序开发中需要适配ViewModel层。

二、ViewModel的数据源

(一)数据源类型及共享范围

  1. @State:组件级别的共享,通过命名参数机制传递,例如:CompA: ({ aProp: this.aProp }),表示传递层级(共享范围)是父子之间的传递。可以初始化多种状态变量,@prop@link和@ObjectLink可以和其建立单向或双向同步。
  2. @Provide:组件级别的共享,可以通过key和@Consume绑定,因此不用参数传递,实现多层级的数据共享,共享范围大于@State。适合在单个页面UI组件树中共享状态数据。
  3. LocalStorage:页面级别的共享,可以通过@Entry在当前组件树上共享LocalStorage实例,可在ArkUI应用程序的几个页面上共享。
  4. AppStorage:应用全局的UI状态存储,和应用进程绑定,在整个应用内的状态数据的共享。是LocalStorage的单例对象,在页面中使用@StorageLink和@StorageProp为多个页面之间共享数据,还可使用PersistentStorage将其特定属性持久化到本地磁盘文件中,再次启动时恢复数据。

(二)不同数据源的使用示例

  1. @State装饰的变量与子组件共享状态数据
    • 例如在Parent组件中使用@State装饰testNum变量,并将其传递给LinkChildSibling子组件。在LinkChild组件中,@Link装饰的testNum与父组件的@State testNum建立双向同步,LinkChild中的更改会同步到父组件Parent,再从Parent同步到Sibling,同时也会同步给LinkChild的子组件LinkLinkChildPropLinkChild。而PropLinkChild中的@Prop和其父组件建立单向同步关系。
  2. @Provide装饰的变量与后代组件共享状态数据
    • 如在Parent组件中使用@Provide装饰testNum变量,在LinkChildSiblingLinkLinkChild等后代组件中使用@Consume创建双向同步,通过绑定相同的key连接,而不是通过组件构造函数参数传递,将更改从父组件传递到孙子组件更加方便。
  3. 给LocalStorage实例中对应的属性建立双向或单向同步
    • 创建LocalStorage实例并通过@Entry(storage)注入根节点,在Parent组件中初始化@LocalStorageLink("testNum")变量,会在LocalStorage实例中创建testNum属性并设置初始值。其子组件使用@LocalStorageLink@LocalStorageProp绑定同一个属性名key来传递数据,@LocalStorageLinkLocalStorage中对应属性的同步行为与@State@Link一致,为双向数据同步。
  4. 给AppStorage中对应的属性建立双向或单向同步
    • LocalStorage类似,在组件中使用@StorageLink@StorageProp为多个页面之间共享数据,如在ParentLinkChildSibling等组件中使用@StorageLink("testNum")@StorageProp("testNum")来共享和操作AppStorage中的testNum属性。

三、ViewModel的嵌套场景

(一)处理复杂类型数据

大多数情况下,ViewModel数据项是复杂类型,如对象数组、嵌套对象或其组合。对于嵌套场景,使用@Observed搭配@Prop或者@ObjectLink来观察变化。推荐设计单独的自定义组件来渲染每一个数组或对象,对于类和数组,@State、@prop@link、@ObjectLink装饰的变量只能观察到第一层的变化,若要观察嵌套类内部对象的变化,可使用@ObjectLink或@Prop,优先考虑@ObjectLink,其通过嵌套对象内部属性的引用初始化自身,性能更好,@prop会对嵌套内部对象进行深度拷贝来初始化,实现单向同步,但性能较慢。

(二)@prop和@ObjectLink嵌套数据结构及区别

  1. 嵌套数据结构示例
    • 父组件ViewB渲染@State arrAArray<ClassA>),@State可观察新数组分配、数组项插入、删除和替换。子组件ViewA渲染每个ClassA对象,使用@ObjectLink a: ClassA可观察嵌套在Array内的ClassA对象的变化(前提是ClassA@Observed装饰)。不使用@Observed时,如ViewBthis.arrA[Math.floor(this.arrA.length/2)].c = 10的操作不会被观察到,相应ViewA组件也不会更新。
  2. @prop和@ObjectLink的区别
    • 当在ViewA中将@ObjectLink替换为@Prop时,@Prop会对嵌套对象进行深度拷贝初始化,性能慢,且在一个ViewA中的属性赋值this.a.c += 1不会引发使用同一个ClassA初始化的其他ViewA的渲染更新,因为它们是不同的拷贝。而@ObjectLink通过引用共享对象,一个ViewA中属性改变会触发所有引用该对象的ViewA更新(前提是ClassA@Observed装饰)。

四、MVVM模式在鸿蒙Next中的优势

(一)简化UI设计和实现

通过ViewModel层的隔离,将UI与数据模型分离,使得UI的设计和实现更加简单,开发者可以专注于UI的展示逻辑,而不必关心数据的获取和存储细节。

(二)提高UI性能

MVVM模式能够更高效地更新UI,当数据发生变化时,只有相关的UI部分会被更新,而不是整个UI重新渲染,从而提高了应用程序的响应速度和性能。

(三)增强代码的可维护性和可测试性

数据和视图的分离使得代码结构更加清晰,易于维护和测试。可以单独对ViewModel层进行单元测试,验证数据的正确性和处理逻辑,同时也方便对UI层进行单独的测试和优化。

总之,鸿蒙Next的MVVM思想为应用程序开发提供了一种有效的架构模式,有助于提高开发效率、提升应用性能和改善代码质量。开发者在实际应用中应合理利用MVVM模式的特性,根据具体需求选择合适的数据源和处理嵌套数据结构的方式,以构建出高质量的应用程序。

Top comments (0)