[HarmonyOS]鸿蒙中MVVM实践(v2版)

1,477 阅读7分钟

[HarmonyOS]鸿蒙中MVVM实践(v2版)

MVVM模式介绍

概念

在现代应用开发中,UI更新需要与数据状态实时同步,而这种同步关系直接影响着应用的性能和用户体验。为了解决数据与UI同步的复杂性,ArkUI采用了 Model-View-ViewModel(MVVM) 架构模式。MVVM通过将应用程序划分为 Model、View和ViewModel 三个核心部分,实现了数据、视图和逻辑的解耦。通过这种模式, UI可以在数据状态变化时自动更新,无需手动干预,从而高效地管理数据和视图的绑定与更新。

  • Model:负责存储和管理应用的数据及业务逻辑,通常从后端接口获取数据。它不直接与UI交互,确保数据的一致性和完整性,是应用的数据基础。

  • View:负责展示UI界面和与用户的交互,不包含任何业务逻辑。View通过与ViewModel的数据绑定来动态更新UI,确保UI的变化与数据的变化同步。

  • ViewModel:作为Model与View之间的桥梁,负责管理UI状态和业务逻辑。它监控Model中数据的变化并通知View更新UI,同时处理用户交互事件并将其转换为数据操作。

ArkUI的UI开发模式采用MVVM架构,通过这种模式,开发者可以轻松实现数据驱动的UI更新,专注于页面的设计,而无需关注繁琐的UI刷新逻辑。数据的维护由状态变量自动更新, 开发者无需感知数据变化的具体实现,所有这些都由ViewModel层完成。因此,使用MVVM模式开发应用,开发者能够高效、简便地管理应用的状态和UI更新。

MVVM架构图

mvvm 1

ArkUI开发模式

以下内容取自官网

mvvm 2

View层

  • 页面组件:所有应用基本都是按照页面进行分类的,比如登录页,列表页,编辑页,帮助页,版权页等。每个页对应需要的数据可能是完全不一样的,也可能多个页面需要的数据是同一套。
  • 业务组件:本身具备本APP部分业务能力的功能组件,典型的就是这个业务组件可能关联了本项目的ViewModel中的数据,不可以被共享给其他项目使用。
  • 通用组件:像内置组件一样,这类组件不会关联本APP中ViewModel的数据,这些组件可实现跨越多个项目进行共享,来完成比较通用的功能。

ViewModel层

  • 页面数据:按照页面组织的数据,用户打开页面时,可能某些页面并不会切换到,因此,这个页面数据最好设计成懒加载的模式。
  • ViewModel层数据和Model层数据的区别:
Model层数据是按照整个工程,项目来组织数据,是一套完成本APP的业务数据。

ViewModel层数据,是提供某个页面上使用的数据,它可能是整个APP的业务数据的一部分。另外ViewModel层还可以附加对应Page的辅助页面显示数据,这部分数据可能与本APP的业务完全无关,仅仅是为页面展示提供便利的辅助数据。

Model层

Model层是应用的原始数据提供者,这一层在UI来看,有两种模式

  • 本地实现:通过纯NativeC++实现

  • 远端实现:通过IO端口(RestFul)实现

鸿蒙中MVVM实践(v2版)

有了上述关于MVVM的一些基础,开始尝试今天的编码实现方案。

开始前的准备

项目代码结构图

只截取本har相关目录结构

harmony_os(项目名)
 |-feat_todolist
    |-bean(存放数据model实体类)
        |-HomeBean.ets
    |-model(model层)
        |-HomeModel.ets
    |-view(view层)
        |-HomeListView.ets
    |-viewmodel(viewmodel层)
        |-HomeViewModel.ets
    |-components(页面层)
        |-MainPage.ets
    |-...(其他模块)

Model层

Model层负责管理应用的数据及业务逻辑,通常与后端服务或数据存储进行交互。在当前demo中,主要作用是用来跟后端数据做交互,也就是直接进行数据的读取操作。

HomeModel类代码如下:

export class HomeModel {
  constructor() {
  }

  // 首页接口
  async getData(uiContext: UIContext, param: string): Promise<ApiResponse<HomeBean>> {
    let requestParams: RequestParams = {
      uiContext: uiContext,
      showLoading: true,
      baseHost: "https://www.wanandroid.com/",
      url: "article/list/0/json",
      data: {
        "a": "aa",
        "b": "bb",
        "p": param
      }
    }
    return httpClient.get<HomeBean>(requestParams)
  }
}

可以看到,HomeModel类中只定义了一个方法,用于获取数据。该方法通过httpClient.get方法发起网络请求,并返回一个Promise对象。只负责做与后端数据交互的任务

ViewModel层

ViewModel层负责管理UI状态和业务逻辑,它与View层进行数据绑定,并监听View层的事件。ViewModel层通常会与Model层进行交互,获取数据并更新UI。

@ObservedV2
export default class HomeViewModel {
  TAG = "debug_HomeViewModel"
  @Trace homeList: HomeBeanDataDatas[] = []

  async loadData(uiContext: UIContext, param: string) {
    let homeModel: HomeModel = new HomeModel()
    let result = await homeModel.getData(uiContext, param)
    console.log(`${this.TAG}: result = ${result}`)

    if (result.data) {
      if (result.data.datas) {
        this.homeList.length = 0 // 移除所有元素
        this.homeList.push(...result.data.datas)
      }
    }
  }

  /**
   * 移除一项
   */
  removeItem(index: number = -1) {
    if (index < 0) {
      return
    }
    if (index > this.homeList.length - 1) {
      return
    }
    this.homeList.splice(index, 1)
  }
}

上述代码中,HomeViewModel类添加了ObservedV2注解,用于数据监听。它定义了一个homeList属性,用于存储获取的数据。loadData方法用于获取数据,并更新homeList属性。removeItem方法用于移除一项数据。 最终 loadData 和 removeItem 方法,都会作为 View 层的 事件驱动,交由 View 来触发调用。

同时会与 Model 进行交互,通过 Model 来获取所需要的数据

View层

View层专注于应用程序的UI展示和用户交互。它仅负责渲染界面和显示数据,不涉及业务逻辑。所有的数据和状态更新由ViewModel层提供, View层通过绑定ViewModel的状态数据来自动更新UI,从而实现视图与数据的解耦。

@ComponentV2
export struct HomeListView {
  TAG = "debug_HomeListView"
  private uiContext: UIContext = this.getUIContext()
  @Param homeViewModel: HomeViewModel = new HomeViewModel()

  aboutToAppear(): void {
    this.homeViewModel.loadData(this.uiContext, "home_page")
    // console.log(`${this.TAG}: homeViewModel.size = ` + this.homeViewModel.homeList.length)
  }

  build() {
    Column() {
      if (this.homeViewModel.homeList && this.homeViewModel.homeList.length > 0) {
        List({ space: 10 }) {
          ForEach(this.homeViewModel.homeList, (item: HomeBeanDataDatas, index: number) => {
            ListItem() {
              if (index % 2 == 0) {
                Text(item.title).fontColor(Color.Black)
              } else {
                Text(item.title).fontColor(Color.Gray)
              }
            }.gesture(
              LongPressGesture({ repeat: false }).onAction((e) => {
                // promptAction.showToast({ message: "长按index=" + index })

                ConfirmDialog.show(this.uiContext, {
                  title: "提示",
                  content: "确定要删除此项吗?index: " + index,
                  cancelText: "暂不",
                  confirmText: "删除吧",
                  onConfirm: (isConfirm) => {
                    // promptAction.showToast({
                    //   message: isConfirm ? "点了确定" : "点了取消",
                    //   duration: 2000
                    // });
                    if (isConfirm) {
                      this.homeViewModel.removeItem(index)
                      promptAction.showToast({
                        message: "已删除~",
                        duration: 2000
                      });
                    }
                  }
                })
              }).onActionEnd((e) => {
              })
            ).onClick(() => {
              promptAction.showToast({ message: "点击index=" + index })
            })
          })
        }
      } else {
        Column() {
          Text("无数据").margin({ top: 20 })
        }
      }
    }.width('100%').height('100%')
  }
}

上述代码是一个简单的列表,通过 ForEach 循环遍历 homeViewModel.homeList,生成一个列表项。 homeList 可监听,所以当前 View 与 ViewModel 实现了绑定,当 homeList 发生变化时,View 会自动更新。

同时,View 可以直接操作 ViewModel 的方法,例如 loadData、removeItem 方法,完成 V 层 到 VM 的事件驱动交互。

总结

在这篇文章中,我们了解了MVVM(Model-View-ViewModel)架构模式及其在应用开发中的重要性。MVVM通过清晰地分离数据层、UI层和逻辑层,使得应用的开发更加高效、可维护,并且提升了代码的可测试性。

通过使用MVVM,开发者能够将应用的业务逻辑与UI展示分离,使得UI的更新与数据状态的变化自动同步,避免了繁琐的手动更新操作。View层专注于用户界面的展示和交互,而ViewModel层则充当了连接数据与UI的桥梁,处理业务逻辑、管理状态变化,并确保数据的一致性。Model层则承担了数据存储和操作的职责,与后端服务或本地数据库进行交互。

通过MVVM模式,开发者能够专注于应用的核心功能和业务逻辑,而不必过多关注UI的刷新和数据同步细节。这不仅提高了开发效率,还使得代码更加模块化,便于后期的扩展与维护。

总的来说,MVVM架构模式不仅使得开发过程更加清晰和高效,还能够为开发者提供一个更灵活、更可扩展的应用框架。对于现代应用开发,尤其是涉及动态数据更新和复杂UI交互的项目,MVVM无疑是一个理想的选择。

源码

上述示例代码,请查看:gitee.com/yanftch/har…