一文读懂鸿蒙mvvm模式

鸿蒙开发—MVVM模式

本文档涵盖了大多数状态管理V1装饰器,所以在阅读本文当前,建议开发者对状态管理V1有一定的了解。 建议提前阅读:[状态管理概述](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-state-management-overview-V5)和状态管理V1装饰器相关文档。

MVVM模式介绍

概念:

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

  • Model:负责存储和管理应用的数据以及业务逻辑,不直接与用户界面交互。通常从后端接口获取数据,是应用程序的数据基础,确保数据的一致性和完整性。
  • View:负责用户界面展示数据并与用户交互,不包含任何业务逻辑。它通过绑定ViewModel层提供的数据来动态更新UI。
  • ViewModel:负责管理UI状态和交互逻辑。作为连接Model和View的桥梁,ViewModel监控Model数据的变化,通知View更新UI,同时处理用户交互事件并转换为数据操作。

ArkUI的UI开发模式就属于MVVM模式,通过对MVVM概念的基本介绍,开发者大致能猜到状态管理能在MVVM中起什么样的作用,状态管理旨在数据驱动更新,让开发者只用关注页面设计,而不去关注整个UI的刷新逻辑,数据的维护也无需开发者进行感知,由状态变量自动更新完成,而这就是属于ViewModel层所需要支持的内容,因此开发者使用MVVM模式开发自己的应用是最省心省力的。

ArkUI模式开发图

ArkUI的UI开发开发模式即是MVVM模式,而状态变量在MVVM模式中扮演着ViewModel的角色,向上刷新UI,向下更新数据,整体框架如下图:
  src

└── ets
├── pages
│ └── Index.ets
├── views
│ ├── TodoComponent.ets
│ ├── AllChooseComponent.ets
│ └── ThingsComponent.ets
├── viewmodels
│ └── TodoListViewModel.ets
└── models
└── TodoListModel.ets

分层说明

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

ViewModel层

  • 页面数据:按照页面组织的数据,用户打开页面时,可能某些页面并不会切换到,因此,这个页面数据最好设计成懒加载的模式。

ViewModel层数据和Model层数据的区别:

Model层数据是按照整个工程,项目来组织数据,是一套完成本APP的业务数据。

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

Model层

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

  • 本地实现:通过纯NativeC++实现
  • 远端实现:通过IO端口(RestFul)实现

注意:

采用本地实现时,系统的对数据加工和处理,基本上一定会存在非UI线程模型,这个时候,被加工的数据变更可能需要即时通知ViewModel层,来引起数据的变化,从而引起UI的相应更新。这个时候,自动线程转换就会变得非常重要。常规下,ViewModel层,View层,都只能在UI线程下执行,才能正常工作。因此需要一种机制,当需要通知UI更新时,需要自动完成线程切换。

分层设计技巧

**Model层**
  • model层存放本应用核心数据结构,这层本身和UI开发关系不大,让用户按照自己的业务逻辑进行封装。

ViewModel层

注意:

ViewModel层不只是存放数据,他同时需要提供数据的服务及处理,因此很多框架会以“service”来进行表达此层。

  • ViewModel层是为视图服务的数据层。它的设计一般来说,有两个特点:1、按照页面组织数据。2、每个页面数据进行懒加载。

View层

View层根据需要来组织,但View层需要区分一下三种组件:

  • 页面组件:提供整体页面布局,实现多页面之间的跳转,前后台事件处理等页面内容。
  • 业务组件:被页面引用,构建出页面。
  • 共享组件:与项目无关的多项目共享组件。

共享组件和业务组件的区别:

业务组件包含了ViewModel层数据,没有ViewModel,这个组件不能运行。

共享组件:不包含任务ViewModel层的数据,他需要的数据需要从外部传入。共享组件包含一个自包含组件,只要外部参数(无业务参数)满足,就可以工作。

代码示例

现在按照MVVM模式组织结构,重构如下:
        • src
          └── ets
          ├── pages
          │ └── Index.ets
          ├── views
          │ ├── TodoComponent.ets
          │ ├── AllChooseComponent.ets
          │ └── ThingsComponent.ets
          ├── viewmodels
          │ └── TodoListViewModel.ets
          └── models
          └── TodoListModel.ets

文件代码如下:

  • Index.ets
import { TodoComponent } from '../views/TodoComponent';
import { AllChooseComponent } from '../views/AllChooseComponent';
import { ThingsComponent } from '../views/ThingsComponent';
import { TodoListViewModel } from '../ viewmodels/TodoListViewModel';

@Entry
@Component
struct Index {
  @State isFinished: boolean = false;
  @State viewModel: TodoListViewModel = new TodoListViewModel();

  build() {
    Column() {
      Row({space: 40}) {
        TodoComponent()
        AllChooseComponent({isFinished: this.isFinished})
      }
      List() {
        ForEach(this.viewModel.todoList, (item: string) => {
          ThingsComponent({isFinished: this.isFinished, things: item})
            .margin(5)
        })
      }
    }
    .height('100%')
    .width('100%')
    .margin({top: 5, bottom: 5})
    .backgroundColor('#90f1f3f5')
  }
}
  • TodoListModel.ets
export class TodoListModel {
  private todoList: string[] = [];

  constructor() {
    // 模拟从后端获取数据,此处为初始化本地数据
    this.todoList = ['7.30 起床', '8.30 早餐', '11.30 中餐', '17.30 晚餐', '21.30 夜宵', '22.30 洗澡', '1.30 起床'];
  }

  getTodoList(): string[] {
    return this.todoList;
  }
}
  • TodoListViewModel.ets
import { TodoListModel } from '../ models/TodoListModel';

export class TodoListViewModel  {
  private model: TodoListModel;
  public todoList: string[] = [];

  constructor() {
    this.model = new TodoListModel();
    this.todoList = this.model.getTodoList();
  }
}
  • TodoComponent.ets
// import { Component } from '@ohos.arkui';

@Component
export struct TodoComponent {
  build() {
    Row() {
      Text('全部待办')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .margin({top: 10, bottom: 10})
  }
}
  • AllChooseComponent.ets
// import { Component, Link } from '@ohos.arkui';

@Component
export struct AllChooseComponent {
  @Link isFinished: boolean;

  build() {
    Row() {
      Button('全选', {type: ButtonType.Normal})
        .onClick(() => {
          this.isFinished = !this.isFinished;
        })
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .backgroundColor('#f7f6cc74')
    }
    .padding({left: 15})
    .width('100%')
    .margin({top: 10, bottom: 10})
  }
}
  • ThingsComponent.ets
// import { Component, Prop, Builder } from '@ohos.arkui';

@Component
export struct ThingsComponent {
  @Prop isFinished: boolean;
  @Prop things: string;

  @Builder displayIcon(icon: Resource) {
    Image(icon)
      .width(28)
      .height(28)
      .onClick(() => {
        this.isFinished = !this.isFinished;
      })
  }

  build() {
    Row({space: 15}) {
      if (this.isFinished) {
        this.displayIcon($r('app.media.yes'));
      } else {
        this.displayIcon($r('app.media.unyes'));
      }
      Text(`${this.things}`)
        .fontSize(24)
        .fontWeight(450)
        .decoration({type: this.isFinished ? TextDecorationType.LineThrough : TextDecorationType.None})
        .onClick(() => {
          this.isFinished = !this.isFinished;
        })
    }
    .height('8%')
    .width('90%')
    .padding({left: 15})
    .opacity(this.isFinished ? 0.3 : 1)
    .border({width: 1})
    .borderColor(Color.White)
    .borderRadius(25)
    .backgroundColor(Color.White)
  }
}

经过MVVM模式拆分之后的代码,项目结构更加清晰,各个模块的职责更加清晰,假如有新的page需要用到事件这个组件,只需要import对应的组件即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值