关于MVVM的理解

本文深入探讨了MVVM架构的本质及其实现方式,澄清了常见误区,解析了ViewModel的角色,并介绍了领域驱动设计(DDD)的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一直想聊聊这个话题,也有朋友跟我留言,让我讲讲MVVM,只可惜一直没整明白,不敢轻易下笔。针对MVVM,网上有很多不错的文章,比如MVVM介绍被误解的 MVC 和被神化的 MVVM以及Look at MVVM from a different perspective等等

文章前我想先提几个问题

  1. MVVM到底是什么?它和MVC有什么区别?
  2. MVVM中VM到底是个什么角色?它和Controller或者Manager有什么区别?
  3. ViewController在MVVM中扮演怎样角色?Api数据请求放在哪里?数据流向如何?

MVVM简介

关于MVVM,相信大家或多或少都有了解。引用MVVM介绍文中一图

受MVC或MVP架构的影响,对MVVM最初印象以为这是一个以ViewModel为核心,处理View和Model的开发架构。于是乎在原有MVC的基础上,创建了一个所谓的ViewModel对象,然后把ViewController中的代码移到ViewModel中,在ViewModel里面处理View以及Model的所有逻辑。毕竟大家都在说MVVM可以为ViewController瘦身,这ViewController就剩创建ViewModel的代码,嗯,够瘦身,这就是MVVM!

慢慢的,发现有什么地方不对,哪里不对?第一想法就是ViewController的定位,View?不是,Controller?也不是!毕竟它就创建ViewModel,好像与View、Model也没啥关系。抛开ViewController不谈,突然发现这样的ViewModel、Model以及View不就是MVC,一个以ViewModel为中心的MVC!

错在哪里

核心问题就在于对ViewModel角色的定位不清!基于MVVM设计思路,ViewModel存在目的在于抽离ViewController中展示业务逻辑,而不是替代ViewController,其它视图操作业务等还是应该放在ViewController中实现

既然不负责视图操作逻辑,ViewModel中就不应该存在任何View对象,更不应该存在Push/Present等视图跳转逻辑。因此,ViewModel中绝不应该存在任何视图操作相关的代码

ViewModel做啥

很简单,处理视图展示逻辑,ViewModel负责将数据业务层提供的数据转化为界面展示所需的VO。其与View一一对应,没有View就没有ViewModel

比如,数据业务层传递一个含有性别属性sex的DO对象,0表示男, 1表示女。ViewModel的职责就是将其转化为展示层可显示的VO对象

ViewModel和View一起组成DDD(Model-Driven Design)领域驱动架构体系中的Presentation展示层。在iOS中,数据流向可以表示为ViewModel->ViewController->View,ViewController负责连接VO及其对应的View对象

领域驱动设计

领域驱动设计(DDD)对于安卓童鞋可能非常熟悉,有兴趣的童鞋可以参考这篇文章,本文不做过多讲解,借用其描述介绍几个名词

  • VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来
  • DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体
  • PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性
  • Domain:领域驱动层,是用户与数据库交互的核心中转站,控制用户数据收集,控制请求转向等

MVVM架构中,ViewModel连接视图View和数据业务Model层,而Domain和Data数据持久层共同组成整个Model层。完整结构如图所示

Model并不表示Model

MVVM架构中的M,并不表示Model对象,而是表示整个数据业务层,对应DDD架构中的Domain层以及数据Data层。业务开发中,一般考虑Api或者DB对象,极少考虑Domain层设计,也不会区分DO或者PO对象。笼统定义Model ,将其传递给展示层ViewModel,久而久之,Model对象承载的信息越来越多,更有甚者,在Model中处理业务逻辑,导致项目维护成本增加,代码中出现if..else的概率也会越来越大

当然,Domain层并不是必须的,实际开发中,需要根据具体复杂度和需求来决定。比如只是纯粹的请求展示界面,设计过多的层次结构反而会增加项目的维护成本。同时,Domain层不应该存在任何状态变量!

Data数据层

ViewModel负责展示层逻辑,而Data层则对应数据层逻辑,一般以Manager或者Service身份存在,数据来源主要包括Api、DB或者Cache等。Data数据层操作对象主要为PO持久化对象,对象一旦创建,原则上不可修改

Data数据层具有独立可测试性,其不依赖视图层而存在。切记不可在Data层操作任何视图对象!

MVVM奇葩说

文章到此,想必各位对MVVM架构已经有了大致了解。对比安卓童鞋对MVP架构的钟爱,iOS童鞋也许更加青睐MVVM,拌上ReactiveCocoa或者RxSwift,这道菜可以做的更加绚烂多彩!当然,正如唐巧在文中所言:ReactiveCocoa 和 MVVM 不应该被神化,我们需要保持的是一个拥抱变化的心,以及理性分析的态度。在新技术的面前,不盲从,也不守旧,一切的决策都应该建立在认真分析的基础上,这样才能应对技术的变化!


写在文后:

为期两天的SwiftCon已经落幕,虽然离我只有两站地铁,只可惜依然没能前往聆听各位大师的技术分享,特别还有我同事兼朋友刘冠杉的个人首秀。虽然,现在网上也出现了各种不好的声音,但是我仍然相信,大多数分享者还是为此付出了不少心血,值得我们为之鼓掌!

个人技术的成长不在一朝一夕,而是长年累月的付出积累沉淀的结果!Swift3.0不久即将发布,而我个人也会逐渐转移☞对Swift语言的学习。与此同时,随着公司ReactNative项目的上马,我也会加强对RN技术的专研和学习中

### MVVM模式概念 MVVM(Model-View-ViewModel)是一种软件架构模式,主要用于构建用户界面。它最早由微软在2005年推出并应用于WPF框架中[^2]。MVVM的设计理念源自经典的MVC模式,但在功能划分上更加专注于数据绑定和逻辑分离。 #### 核心组件 1. **Model**: 表示应用程序中的数据模型以及业务逻辑部分。它是独立于视图的存在,并不关心如何展示数据。 2. **View**: 负责呈现用户界面的部分。它的职责仅限于显示来自`ViewModel`的数据并与之交互。 3. **ViewModel**: 这是MVVM的关键所在。它充当了连接`View`和`Model`的桥梁角色。一方面向`View`提供可观察的数据源以便实现双向绑定;另一方面通过接口或者服务与`Model`通信获取实际所需的信息[^1]。 ### 实现机制 为了更好地理解MVVM的实际应用, 下面以一个简单的例子说明其工作流程: 假设我们有一个待办事项列表(Todo List),其中包含添加新任务的功能: ```javascript // 定义 Model 部分 class TodoModel { constructor() { this.todos = []; } addTodo(todoText) { const newId = this.todos.length + 1; this.todos.push({ id: newId, text: todoText }); } } // 创建 ViewModel 来管理状态变化 const viewModel = new Vue({ el: '#app', data: { todos: [], newTodoText: '' }, methods: { addNewTodo() { if (this.newTodoText.trim()) { // 确保输入不是空白字符 model.addTodo(this.newTodoText); // 更新model this.todos = [...model.todos]; // 同步到viewModel this.newTodoText = ''; // 清空输入框 } } } }); // 初始化 Model 并填充一些初始数据 let model = new TodoModel(); model.addTodo('学习JavaScript'); model.addTodo('练习Vue.js'); // 将初始数据同步至 ViewModel viewModel.todos = [...model.todos]; ``` 在这个案例里,当用户提交一个新的todo项时,会触发 `addNewTodo()` 方法。此方法不仅修改了本地存储(`data`)里的副本还调用了真正的后台操作即更新原始model实例的内容。由于采用了响应式的编程风格(Vue内置特性), 所有依赖这些变量的地方都会自动重新渲染出来而无需手动干预DOM节点的操作过程[^1]。 另外值得注意的是,在某些平台比如Android开发环境下还可以利用Data Binding Library进一步简化上述代码结构从而减少样板代码量同时增强程序健壮性和维护便利度等问题[^3]。 ### 数据流方向 在一个典型的MVVM系统里面存在两种主要的数据流动形式: - **单向数据流**:从`ViewModel`流向`View`, 当前者的属性发生变化之后后者能够立刻反映出最新的改变情况; - **双向数据绑定**:允许开发者既可以从控件读取当前值也可以反过来设置它们的新数值进而影响对应的字段内容。这种技术特别适合表单验证场景下的即时反馈需求等等[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值