HarmonyOS-NEXT中认识MVVM模式以及MVC和MVP架构模式以及数据的状态管理

软件架构设计模式

有三种主要的设计模式分别是mvvm和mvc以及mvp设计模式,主要通过分离关注点的方式来组织代码结构,优化我们的开发效率

Contraller/Presenter负责业务逻辑,Model负责管理数据。View负责显示。

假设我们在进行项目开发的时候,使用单页应用时,往往一个路由页面对应了一个脚本文件,所有的页面逻辑都在一个脚本文件里,如果项目过大,那么代码就会非常冗长,难以维护。

mvc模式

  • MVC通过分离Model、View和Contraller的方式来组织代码结构。其中View负责页面的显示逻辑,Model负责存储页面的业务数据以及对相应数据的操作。
  • 并且View和Model应用了观察者模式,当Model层发生改变的时候,他会通知有关View层更新页面
  • Contraller层是View层和Model层的纽带,主要负责用户与应用的响应操作,View和Model是相互解耦的,他们不直接交互,而是通过Controller来进行通信。当用户与页面产生交互的时候,Controller根据事件的类型和用户输入处理相关的业务逻辑,并更新Model中的数据。然后Model层再去通知View层更新。
  • 在MVC中,View需要主动从Model中获取数据,并由Controller负责将数据传递给View进行展示。这使得开发者需要手动编写代码来同步更新数据和UI,相对来说,MVVM的数据绑定机制在这方面更加简化了开发流程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在开发过程中十分繁琐,不灵活,都是单向的,一个小的事件操作,都必须要经过这样的流程,开发不便捷

在这里插入图片描述

这种模式在开发中更加灵活,但是这种灵活也伴随着问题:

  1. 数据流混乱:
    在这里插入图片描述

  2. View比较庞大,而Controller比较单薄:由于很多开发者都会在View中写一些逻辑代码,导致View中的内容越来越庞大,而Controller变得越来越单薄(单纯的数据监听)

mvp模式

  • MVP通过分离Model、View和Presenter的方式来组织代码结构其中View负责页面的显示逻辑,Model负责存储页面的业务数据以及对相应数据的操作。
  • 并且View和Model应用了观察者模式,当Model层发生改变的时候,他会通知有关View层更新页面

在这里插入图片描述

  • MVP模式与MVC唯一不同的在于Presenter和Contraller
    • 在MVC模式中我们使用观察者模式,来实现Model层数据发生变化时通知View层的更新,这样View层和Model层耦合在一起,当项目逻辑变得复杂的时候,可能会造成代码的混乱,可能会对代码的复用性造成一些问题。
    • MVP模式通过使用Presenter来实现对View层和Model层的解耦,MVC中的Contraller只知道Model的接口,因此它没有办法控制View层的更新,MVP模式中,View层的接口暴露给了Presenter,因此可以在Presenter中将Model的变化和View的变化绑定在一起,以此来实现View和Model的同步更新,实现了对View和Model的解耦
  • Presnter还包含了其他的响应的逻辑

MVVM模式

在这里插入图片描述

  • MVVM通过分离Model、View和ViewModel的方式来组织代码结构其中

  • View:是用户界面的可视化部分,负责展示数据并与用户进行交互。View通常由XML、HTML、XAML等描述,这取决于具体的开发平台。在ArkUI中为@Components修饰组件渲染的UI

  • Model:表示应用程序的数据模型或业务逻辑,负责处理数据的存取、处理和操作。它通常包含数据结构、数据库操作、网络请求等。Model并不直接与UI层交互,它只暴露一些接口供ViewModel层调用,使得ViewModel可以获取所需的数据。

  • ViewModel:层类似媒婆,起到了牵线搭桥的作用,负责将数据从Model中取出并转换成View可用的形式。ViewModel不直接操作View,而是通过数据绑定机制将数据与View进行绑定,使得数据的变化可以自动反映在View上,实现了数据的双向绑定。在ArkUI中,ViewModel是存储在自定义组件的状态变量。

    • 自定义组件通过执行其build()方法或者@Builder装饰的方法来渲染UI,即ViewModel可以渲染View。
    • View可以通过相应event handler来改变ViewModel,即事件驱动ViewModel的改变,另外ViewModel提供了@Watch回调方法用于监听状态数据的改变。
    • 在ViewModel被改变时,需要同步回Model层,这样才能保证ViewModel和Model的一致性,即应用自身数据的一致性。
    • ViewModel结构设计应始终为了适配自定义组件的构建和更新,这也是将Model和ViewModel分开的原因。
    • ViewModel通常也包含用户交互的逻辑,例如处理用户输入、按钮点击等
    • 它的思想和MVP其实是相同的,不过ViewModel通过双向的数据绑定,将View和Model的同步更新给自动化了,当Model发生变化时,ViewModel就会自动更新,ViewModel变化了,View也会更新,这样就将Presenter中的工作给自动化了。
      在这里插入图片描述
  • MVVM在保持View和Model松耦合的同时,还减少了维护它们关系的代码,使用户专注于业务逻辑,兼顾开发效率和可维护性

ViewModel的数据源

ViewModel包含多个数据源。@State和@Provide装饰的变量以及LocalStorage和AppStorage都是顶层数据源,其余装饰器都是与数据源做同步的数据。装饰器的选择取决于状态需要在自定义组件之间的共享范围。共享范围从小到大的排序是:

  • @State:组件级别的共享,通过命名参数机制传递,例如:CompA: ({ aProp: this.aProp }),表示传递层级(共享范围)是父子之间的传递。
  • @Provide:组件级别的共享,可以通过key和@Consume绑定,因此不用参数传递,实现多层级的数据共享,共享范围大于@State。
  • LocalStorage:页面级别的共享,可以通过@Entry在当前组件树上共享LocalStorage实例。
  • AppStorage:应用全局的UI状态存储,和应用进程绑定,在整个应用内的状态数据的共享。
@State装饰的变量与一个或多个子组件共享状态数据

@State可以初始化多种状态变量,@Prop、@Link和@ObjectLink可以和其建立单向或双向同步。

  1. 使用Parent根节点中@State装饰的testNum作为ViewModel数据项。将testNum传递给其子组件LinkChild和Sibling。
// xxx.ets
@Entry
@Component
struct Parent {
   
   
  @State @Watch("testNumChange1") testNum: number = 1;

  testNumChange1(propName: string): void {
   
   
    console.log(`Parent: testNumChange value ${
     
     this.testNum}`)
  }

  build() {
   
   
    Column() {
   
   
      LinkChild({
   
    testNum: $testNum })
      Sibling({
   
    testNum: $testNum })
    }
  }
}
  1. LinkChild和Sibling中用@Link和父组件的数据源建立双向同步。其中LinkChild中创建了LinkLinkChild和PropLinkChild。
@Component
struct Sibling {
   
   
  @Link @Watch("testNumChange") testNum: number;

  testNumChange(propName: string): void {
   
   
    console.log(`Sibling: testNumChange value ${
     
     this.testNum}`);
  }

  build() {
   
   
    Text(`Sibling: ${
     
     this.testNum}`)
  }
}

@Component
struct LinkChild {
   
   
  @Link @Watch("testNumChange") testNum: number;

  testNumChange(propName: string): void {
   
   
    console.log(`LinkChild: testNumChange value ${
     
     this.testNum}`);
  }

  build() {
   
   
    Column() {
   
   
      Button('incr testNum')
        .onClick(() => {
   
   
          console.log(`LinkChild: before value change value ${
     
     this.testNum}`);
          this.testNum = this.testNum + 1
          console.log(`LinkChild: after value change value ${
     
     this.testNum}`);
        })
      Text(`LinkChild: ${
     
     this.testNum}`)
      LinkLinkChild({
   
    testNumGrand: $testNum })
      PropLinkChild({
   
    testNumGrand: this.testNum })
    }
    .height(200).width(200)
  }
}
  1. LinkLinkChild和PropLinkChild声明如下,PropLinkChild中的@Prop和其父组件建立单向同步关系。
@Component
struct LinkLinkChild {
   
   
  @Link @Watch("testNumChange") testNumGrand: number;

  testNumChange(propName: string): void {
   
   
    console.log(`LinkLinkChild: testNumGrand value ${
     
     this.testNumGrand}`);
  }

  build() {
   
   
    Text(`LinkLinkChild: ${
     
     this.testNumGrand}`)
  }
}


@Component
struct PropLinkChild {
   
   
  @Prop @Watch("testNumChange") testNumGrand: number = 0;

  testNumChange(propName: string): void {
   
   
    console.log(`PropLinkChild: testNumGrand value ${
     
     this.testNumGrand}`);
  }

  build() {
   
   
    Text(`PropLinkChild: ${
     
     this.testNumGrand}`)
      .height(70)
      .backgroundColor(Color.Red)
      .onClick(() => {
   
   
        this.testNumGrand += 1;
      })
  }
}

在这里插入图片描述

当LinkChild中的@Link testNum更改时。

  1. 更改首先同步到其父组件Parent,然后更改从Parent同步到Sibling。
  2. LinkChild中的@Link testNum更改也同步给子组件LinkLinkChild和PropLinkChild。

@State装饰器与@Provide、LocalStorage、AppStorage的区别:

  • @State如果想要将更改传递给孙子节点,需要先将更改传递给子组件,再从子节点传递给孙子节点。
  • 共享只能通过构造函数的参数传递,即命名参数机制CompA: ({ aProp: this.aProp })。

完整的代码示例如下:

@Component
struct LinkLinkChild {
   
   
  @Link @Watch("testNumChange") testNumGrand: number;

  testNumChange(propName: string): void {
   
   
    console.log(`LinkLinkChild: testNumGrand value ${
     
     this.testNumGrand}`);
  }

  build() {
   
   
    Text</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值