【HarmonyOS——MVVM模式 | 理解MVVM模式,看这一篇就够了】 ...

打印 上一主题 下一主题

主题 1012|帖子 1012|积分 3036

大家好,我是学徒小z,近期项目开发中遇到一些数据源放置杂乱的题目,所以带来一篇MVVM模式的文章

  
MVVM模式

为什么要用MVVM模式

举一个例子,小明是一个开发者,学习了很多根本的知识之后,迫不及待去开发一款APP,这是他第一次开发自己的APP,没有什么经验,所以没有注重APP的项目布局,随着完成了简单的登录注册功能,小明以为开发一个APP应该很快就能完成,但随着功能模块的增加,一些棘手的题目出现了,数据源的地方到处都有,不知道从哪个地方调用,另有一些重复的地方;想要获取某些数据,发现正常的办法根本行不通,于是使用耗费性能的办法去办理,等等。久而久之,APP最后是写完了,但想要添加新的需求,这个地方要改,谁人地方也要改,一不警惕,改了一大片,运行不了了。
所以MVVM模式就是要去办理这些题目的,引导开发者更容易的开发和维护产品。
对于鸿蒙中MVVM模式的疑惑

其实很多小伙伴在初次打仗到鸿蒙中的MVVM都会感到一些狐疑,就比如,诶,为什么找不到viewmodel层在哪里,应该在viewmodel里面放什么东西,在model里面方什么东西,为什么又说@State、@Link就是viewmodel呢等等题目,这些题目,我接下来以我的理解讲一下。
首先就是鸿蒙中的MVVM模式,分为ArkUI的MVVM模式和项目布局中的MVVM模式。这里分开讲是为了讲得清楚一些,其实它们中的viewmodel是一体的,在项目布局中写一个viewmodel层,布局能更加清楚。
ArkUI的MVVM



  • ArkUI接纳了MVVM模式,其中ViewModel将数据与视图绑定在一起,更新数据的时候直接更新视图。
    也就是说,一系列装饰器本事就实现了viewmodel的本领,ArkUI中的viewmodel就是装饰器装饰的状态变量。
    关于状态管理最佳实践,本文不再细讲,要不然跑题了,感爱好的可点击前面的链接
  • 至于model和view,和项目布局中的MVVM一起来讲



项目布局中的MVVM

1. 概述



  • 应用开发中,UI的更新状态需要随着数据的更新而同步更新,这种同步通常决定了应用的性能和用户体验。为了办理UI和数据同步的复杂性,ArkUI接纳了Model-View-ViewModel的架构模式。通过这种模式,UI可以随着状态的变革主动更新,无需手动处理。

    • Model:处理与应用数据相关的业务逻辑和数据访问层。它通常包括数据布局、服务层调用和与数据库操作。
    • View:没有业务逻辑,只管保持“傻瓜化”,通过数据绑定和变乱监听与ViewModel交互。
    • ViewModel:负责管理UI状态和交互逻辑。作为毗连Model和View的桥梁,ViewModel监控Model数据的变革,关照View更新UI,同时处理用户交互变乱并转换为数据操作

  • 状态管理在MVVM模式中扮演者ViewModel的角色,向上革新UI,向下更新数据,整体框架如下图

2 .分层说明



  • view

    • 页面组件,比如登录页、列表页等。页面的数据源有可能是相同的,也可能是不同的。
    • 业务组件:自己具备本APP部分业务本领的功能组件,典型的就是这个业务组件可能关联了本项目标ViewModel中的数据,不可以被共享给其他项目使用。
    • 通用组件:和内置组件一样,不会关联到ViewModel中的数据

  • ViewModel层

    • 在用户进入页面的时候,页面的有些数据不一定被访问到,所以最好讲页面数据计划成懒加载的模式。
    • 和model的区别:Model层数据是整个项目标数据,而ViewModel负责对应页面的数据,是整个APP业务数据的一部分,同时进行逻辑处理,也就是对Model的数据继续一定的处理,进行对View的渲染。
    • ViewModel层不光是存放数据,他同时需要提供数据的服务及处理,因此很多框架会以“service”来进行表达此层。

  • Model层

    • 重要负责整个应用的原始数据和数据库操作
    • 一些网络哀求获取的数据放在这里,网络哀求可以另写为工具类,在该层是调用工具获取原始数据

3. 架构核心原则

不可跨层访问



  • View层不可以直接调用Model层的数据,只能通过ViewModel提供的方法进行调用。
  • Model层数据,不可以直接操作UI,Model层只能关照ViewModel层数据有更新,由ViewModel层更新对应的数据
下层不可访问上层数据

下层的数据通过关照模式更新上层数据。在业务逻辑中,下层不可直接写代码去获取上层数据。如ViewModel层的逻辑处理,不能去依靠View层界面上的某个值。
举例说明一下
  1. //错误示例,不符合MVVM原则
  2. @Entry
  3. @Component
  4. export default class MyView extends View {
  5.     private viewModel = new MyViewModel();
  6.     @State userInput: string = '';
  7.     build() {
  8.         Column() {
  9.             TextInput()
  10.                 .onChange((value) => this.userInput = value);
  11.             Button('Process')
  12.                 .onClick(() => {
  13.                     // ViewModel直接访问View的状态
  14.                     this.viewModel.processInput(this.userInput);
  15.                 });
  16.         }
  17.     }
  18. }
  19. export class MyViewModel {
  20.     processInput(input: string) {
  21.         // 处理逻辑(假设将字符串变为大写)
  22.         console.log('Processed:', input.toUpperCase());
  23.     }
  24. }
复制代码
  1. //正确示例,在MVVM中,ViewModel不应直接依赖View,而是通过数据绑定来进行交互:
  2. @Entry
  3. @Component
  4. export default class MyView extends View {
  5.     private viewModel = new MyViewModel();
  6.     @State userInput: string = '';
  7.     @Link processedOutput: string = this.viewModel.processedOutput;
  8.     build() {
  9.         Column() {
  10.             TextInput()
  11.                 .onChange((value) => this.userInput = value);
  12.             Button('Process')
  13.                 .onClick(() => {
  14.                     // 调用ViewModel方法,不直接传递View的值
  15.                     this.viewModel.processInput(this.userInput);
  16.                 });
  17.             // 显示处理后的输出
  18.             Text(this.processedOutput);
  19.         }
  20.     }
  21. }
  22. export class MyViewModel {
  23.     @State processedOutput: string = '';
  24.     processInput(input: string) {
  25.         // 处理逻辑
  26.         this.processedOutput = input.toUpperCase();
  27.     }
  28. }
复制代码
非父子组件间不可直接访问

这是针对View层计划的核心原则,一个组件应该具备如许的逻辑:


  • 禁止直接访问父组件
    使用eventhub,或者通过在父组件中定义个一个回调函数,将他传给子组件A,子组件通过该函数进行数据处理,返回给父组件,父组件传给子组件B
  • 禁止直接访问兄弟组件本领。这是由于组件应该仅能访问自己看的见的子节点(通过传参)和父节点(通过变乱或关照),以此完成组件之间的解耦。
    1. // ParentComponent.ts
    2. @Entry
    3. @Component
    4. export default class ParentComponent {
    5.     @State sharedData: string = '';
    6.     handleDataChange(newData: string) {
    7.         this.sharedData = newData; // 更新状态,触发子组件的重新渲染
    8.     }
    9.     build() {
    10.         Column() {
    11.             ComponentA({ onDataChange: (data) => this.handleDataChange(data) })
    12.             ComponentB({ sharedData: this.sharedData })
    13.         }
    14.     }
    15. }
    16. // ComponentA.ts
    17. export default class ComponentA {
    18.     @Prop onDataChange: (data: string) => void;
    19.     someMethod() {
    20.         this.onDataChange('new value'); // 通过回调通知父组件
    21.     }
    22. }
    23. // ComponentB.ts
    24. export default class ComponentB {
    25.     @Prop sharedData: string; // 接收父组件的状态
    26.     build() {
    27.         Text(this.sharedData); // 渲染来自父组件的数据
    28.     }
    29. }
    复制代码
  • 我们举一个场景例子说明
    想象一家大公司里有不同的员工和部门,每个部门可以看作一个组件,公司里的每个员工则代表组件中的逻辑或功能。为了确保公司运作高效,各部门之间需要遵循一定的沟通流程:
    总司理代表父组件,负责协调和管理整个公司。
    销售部和财政部代表两个子组件(ComponentA 和 ComponentB)。
    员工之间的交流需要遵循一定的规则,不能随意跨部门沟通。
    错误的沟通方式(违背原则的做法)
    假设销售部的员工小张需要财政部的员工小李帮忙完成一份财政报表。假如小张直接跑到小李的办公室说:“小李,帮我做这份报表”,这就是组件直接相互访问。虽然可以完成工作,但会导致以下题目:

    • 干扰:小张打断了小李的工作流程,造成工作杂乱。
    • 杂乱:不同部门的人随意要求会造成信息不对称,影响其他工作的协调。
    正确的沟通方式(符合解耦原则)
       

    • 通过总司理中转
      在规范的公司里,小张不会直接找小李,而是向总司理(父组件)陈诉需求:“我需要一份财政报表。”总经剖析根据工作安排关照财政部,由财政部的员工小李完成这份工作。
      解释
      **销售部(ComponentA)和财政部(ComponentB)**之间没有直接联系。
      总司理(父组件)作为中间人,协调工作和信息流。
      如许做避免了部门之间的直接依靠,确保了各部门的独立性和工作有序性。
    • 通过公司公告(变乱总线)
      另一种沟通方式是在公司公告栏发布信息。比方,小张将哀求写在公告栏上:“需要财政报表”。任何有本领处理这件事的员工,比如小李,就会看到公告并主动相应。这类似于使用变乱总线进行通讯。
      解释
      小张不需要知道具体是谁来处理,只需发布哀求。
      小李看到公告并进行处理,小张和小李之间没有直接沟通。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立聪堂德州十三局店

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表