浅谈MVC和MVVM

面试中被问到MVVM,结果只是知道有这个东西,具体原理不清楚,下来在网上找了各位大神的讲解,总结如下,希望能对大家有帮助。

一、MVC

MVC框架是业界广泛接受的一种前端应用框架类型,这种框架把应用分为三个部分:
  • Model(模型)负责管理数据,大部分业务逻辑也应该放在Model中;
  • View(视图)负责渲染用户界面,应该避免在View中涉及业务逻辑;
  • Controller(控制器)负责接受用户输入,根据用户输入调用对应的Model部分逻辑,把产生的数据结果交给View部分,让View渲染出必要的输出。

通信方式如下:

  1. view传送指令到controller
  2. controller完成业务逻辑后,model改变状态
  3. model将新的数据发送到view,用户得到反馈
MVC框架的几个组成部分和请求的关系可以用下图表示:
但是MVC若使用不当,很可能让大量代码都集中在Controller之中,MVC就变成了Massive View Controller。
MVC设计模式的原则:Don't repeat yourself。要求能够复用的代码要尽量复用,来保证重用。
在 MVC 这种设计模式中,我们发现 View 和 Model 都是符合这种原则的
对于 View 来说,你如果抽象得好,那么一个 App 的动画效果可以很方便地移植到别的 App 上,而 Github 上也有很多 UI 控件,这些控件都是在 View 层做了很好的封装设计,使得它能够方便地开源给大家复用。比如上一个项目中我们用到的前端UI控件ant design,这是一个很好的控件库,所有的UI都已经封装好,随时可以拿来用,不用自己再编写底层的样式实现。
对于 Model 来说,它其实是用来存储业务的数据的,如果做得好,它也可以方便地复用。当然,因为和业务本身的数据意义相关,Model 层的复用大多数是在一个产品内部,不太可能像 View 层那样开源给社区。
如果我们能够意识到 Controller 里面的代码不便于复用,我们就能知道什么代码应该写在 Controller 里面了,那就是那些不能复用的代码。在我看来,Controller 里面就只应该存放这些不能复用的代码,这些代码包括:
  • 在初始化时,构造相应的 View 和 Model。
  • 监听 Model 层的事件,将 Model 层的数据传递到 View 层。
  • 监听 View 层的事件,并且将 View 层的事件转发到 Model 层。
如果 Controller 只有以上的这些代码,那么它的逻辑将非常简单,而且也会非常短。
如何对ViewController瘦身呢,就引入了MVVM。

二、MVVM

MVVM 在使用当中,通常还会利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化。

mvvm通信:唯一的区别是MVVM采用了双向数据绑定,view的变动自动反映在view-model。

    

在 MVVM 框架中,View(视图) 和 Model(数据) 是不可以直接通讯的,在它们之间存在着 ViewModel 这个中间介充当着观察者的角色。当用户操作 View(视图),ViewModel 感知到变化,然后通知 Model 发生相应改变;反之当 Model(数据) 发生改变,ViewModel 也能感知到变化,使 View 作出相应更新。这个一来一回的过程就是我们所熟知的双向绑定。

对此,MVVM 的作者 John Gossman 的批评应该是最为中肯的。John Gossman 对 MVVM 的批评主要有两点:
  • 数据绑定使得 Bug 很难被调试。你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
  • 对于过大的项目,数据绑定需要花费更多的内存。

三、VC 到 VM 的转变

一个 VC 在包含了大量的业务逻辑后,代码就会变得特别的臃肿、不易阅读和修改。于是后来就慢慢延伸出了MVVM 模式。MVVM 模式顾名思义是由 Model、View 和 ViewModel(下称 VM )组成,之前的 VC 变成了 VM。怎么来理解 VM 呢?
对于一个 Model ,比如我们要存储和显示一个人的信息,这个人具有姓名、年龄、性别这三个属性。这个Model的伪代码如下:
class Person {    
    String name;    
    int age;    
    int gender;
}
Model 的数据模型,和我们的业务需求或者说业务实体(Entity)是一一映射关系。而 ViewModel 顾名思义,就是一个 Model of View,它是一个 View 信息的存储结构,ViewModel 和 View 上的信息是一一映射关系。
以一个软件的登陆场景为例子,假设这个登录界面上有如下逻辑:
  • 用户名输入框
  • 密码输入框
  • 登陆按钮,点击登陆按钮按钮置灰,显示 loading 框
  • 登陆成功,页面触发跳转
  • 登陆失败,loading 框消失,在界面上显示错误信息
  • 错误信息可以分为两种情况: 1、密码错误;2、没有网络
那么我们下面来定义这样一个 ViewModel:
class LoginViewModel {    
    String  userId;    
    String  password;    
    bool    isLoading;    
    bool    isShowErrorMessage;    
    String  errorMessage;
}

界面初始化

由于 LoginView 和 LoginViewModel 是映射关系,也称为绑定关系,那么 LoginViewModel 是怎样的数据,View 就按照怎样的数据来进行显示。界面第一次打开时,整个 LoginViewModel 的初始值为:
{    
    userId: '',    
    password: '',    
    isLoading: false,    
    isShowErrorMessage: false,    errorMessage: ''
}
那么此时界面为:
  • 用户名输入框显示为空白字符串
  • 密码输入框显示为空白字符串
  • loading 框因为 isLoading = false,所以不显示
  • 错误信息框 因为 isShowErrorMessage = false,所以不显示
  • 错误信息框里面的文字为空白字符串

触发登陆

接下来,用户输入用户名和密码,点击登录按钮,在登陆事件里面触发网络通信逻辑,同时设定 isLoading = true,伪代码如下:
function onLoginButtonClick() {   
    request(url, ...);    
    loginViewModel.isLoading = true;
}
此时 LoginViewModel 的值为:
{    
    userId: 'this is user id',    
    password: 'this is password',    
    isLoading: true,    
    isShowErrorMessage: false,    
    errorMessage: ''
}
随着 isLoading 值的变化,因为 ViewModel 和 View 存在绑定关系,那么此时界面动态变化为:
  • 用户名输入框显示为刚刚输入的字符串
  • 密码输入框显示为刚刚输入的字符串
  • 因为isLoading = true,所以显示 loading 框
  • 因为isLoading = true,登陆按钮置灰,不可点击
  • 错误信息框 因为 isShowErrorMessage = false,所以不显示
  • 错误信息框里面的文字为空白字符串

登录失败

接下来我们假设登陆失败,服务器返回密码错误,那么此时在服务器逻辑的相应代码里面我们去设定 isLoading = false,isShowErrorMessage = true,以及对应的errorMessage,伪代码如下:
request(url, {    
    success: function() { 
       ...    
    },    
    fail: function(err) {       
      if(err.code == 1000)  { // 假设1000表示密码错误            
          loginViewModel.isLoading = false;            
          loginViewModel.isShowErrorMessage = true;            
          loginViewModel.errorMessage = '密码错误';        
        }   
   }
})
此时 LoginViewModel 的值为:
{    
    userId: 'this is user id',   
    password: 'this is password',    
    isLoading: false,    
    isShowErrorMessage: ture,    
    errorMessage: '密码错误'
}
接下来依然是触发界面变化,根据绑定关系重新更新显示内容:
  • 用户名输入框显示为刚刚输入的字符串
  • 密码输入框显示为刚刚输入的字符串
  • 因为isLoading = false,隐藏 loading 框
  • 因为isLoading = false,登陆按钮置为正常,可以点击
  • 因为 isShowErrorMessage = true,显示错误信息框
  • 错误信息框里面的文字为“密码错误”
个人理解,在广义地谈论MVC架构时,并非指本文中严格定义的MVC,而是指的MV*,也就是视图和模型的分离,只要一个框架提供了视图和模型分离的功能,我们就可以认为它是一个MVC框架。在开发深入之后,可以再体会用到的框架到底是MVC还是MVVM。

参考链接:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值