MVC&MVVM

MVC不是框架,不是设计模式,也不是软件架构,而是一种架构模式。

● 框架(Framework):是一个系统的可重用设计,表现为一组抽象的可交互方法。它就像若干类的构成,涉及若干构件,以及构件之间的相互依赖关系、责任分配和流程控制等。比如,C++语言的QT、MFC、GTK, Java语言的SSH、SSI, PHP语言的Smarty(MVC模式), Python语言的Django(MTV模式)等。
● 设计模式(Design Pattern):是一套被反复使用、多数人知晓的、经过分类的代码设计经验总结。其目的是为了代码的可重用性、让代码更容易被他人理解、保证代码的可靠性。比如,工厂模式、适配器模式和策略模式等。
● 软件架构(Software architecture):是一系列相关的抽象模式,用于指导大型软件系统各个方面的设计。软件架构是一个系统的草图,软件体系结构是构建计算机软件实践的基础。
● 架构模式(风格):也可以说成框架模式,一个架构模式描述软件系统里基本的结构组织或纲要。架构模式提供一些事先定义好的子系统,指定它们的责任,并给出把它们组织在一起的法则和指南。一个架构模式常常可以分解成很多个设计模式的联合使用。MVC模式就属于架构模式,还有MTV、MVP、CBD和ORM等。

MVC

MVC模式是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

模型负责数据的持久化(也就是存储);视图负责数据的输入和显示,直接和用户交互的一层,如大家看到的网站的页面内容、在表单上输入的数据;控制器负责具体的业务逻辑,根据用户的请求内容操作相应的模型和视图。

Controller本身不输出任何内容,也不做任何处理,它只是接收请求并决定调用哪个模型构件去处理请求,然后确定用哪个视图来显示返回的数据。

中介模式通过一个中介对象来封装一系列的对象交互,使多个对象之间不需要显式地相互引用,从而使其耦合松散。MVC模式可以理解成对中介模式的一种延伸

外观模式的核心思想是:用一个简单的接口来封装一个复杂的系统,使这个系统更容易使用,也就是对软件进行分层,不同的层实现不同的功能。而 MVC 模式将这一思想应用到了极致

MVP

MVP是MVC的一个变种,在MVP中使用Presenter对视图和模型进行解耦。Presenter相当于MVC中的Controller,负责业务逻辑的处理;Model和View不能直接通信,只能通过Presenter间接地通信

Java的Spring MVC就是用的这种模式

MVT

MTV 模式本质上与 MVC 模式没有什么差别,也是各组件之间保持松耦合关系,只是定义上有些许不同。

Model(模型):负责业务对象与数据库的对象(ORM)。
Template(模板):负责如何把页面展示给用户。
View(视图):负责业务逻辑,并在适当的时候调用Model和Template。

Python的Django就是用这种模式实现的

MVVM

MVVM(Model-View-ViewModel)最早由微软提出,ViewModel指“Model of View”,即“视图的模型”,它将View的状态和行为抽象化,让我们可以将UI和业务逻辑分开,将原来MVC中的C(Controller)用VM(ViewModel)来取代,相当于对MVC做了拓展。

在MVP中,Presenter负责协调和控制Model与View的工作,保证Model和View的数据实时同步和更新,但这个操作需要程序员写代码手动控制。而MVVM中ViewModel把View和Model的同步逻辑自动化了,以前Presenter负责的View和Model同步不再需要手动地进行操作,而是交给框架所提供的数据绑定功能来负责,只需要告诉它View显示的数据对应的是Model的哪一部分即可。

除了熟悉的这3部分之外,其实在MVVM的实现中还引入了一个隐式的Binder层,声明式数据和命令绑定在MVVM模式中就是通过它实现的。DataBinding是一个实现数据和View层绑定的框架,是构建MVVM模式的关键工具。

双向数据绑定可以简单地理解为一个模板引擎,当视图改变时更新模型,当模型改变时更新视图。不同的框架实现双向数据绑定的技术有所不同,一般有以下3种

  • 数据劫持
  • 发布-订阅模式
  • 脏值检查

Vue采用数据劫持和发布-订阅模式的方式。

  • Observer:数据监听器
  • Compiler:指定解析器
  • Watcher:订阅者

Observer用于监听数据变化,如果数据发生改变,不论是在View层还是Model层,Oberver都会知道,然后告诉Watcher。Compiler的作用是对数据进行解析,之后绑定指定的事件,在这里主要用于更新视图。
Vue.js数据绑定的流程:首先将需要绑定的数据用数据劫持方法找出来,之后用Observer监听这堆数据,如果数据发生变化,Observer就会告诉Watcher,然后Watcher会决定让哪个Compiler去做出相应的操作,这样就完成了数据的双向绑定。

虚拟DOM

虚拟DOM只是MVVM框架的一种实现方案,二者没有好坏之分。在流行的前端框架中,除了React采用虚拟DOM之外,其他MVVM系框架,如Angular、Vue、Avalon,采用的都是数据绑定。

当有数据变化要进行对应的操作时,React检查是DOM结构层面的,而MVVM的检查则是数据层面的。MVVM的性能检测也根据检测层面的不同而有所不同:Angular的脏检查使得任何变动都会产生固定的更新的代价;而Vue/Avalon采用的依赖收集,使得在JS和DOM层面都会产生更新。

· 脏检查:scope digest +必要DOM更新
· 依赖收集:重新收集依赖+必要DOM更新

可以看出,Angular效率低的地方在于任何小变动都会引起界面的重绘,但是,当所有数据都变化的时候,Angular并不吃亏。依赖收集在初始化和数据变化的时候都需要重新收集依赖,在数据流比较小的时候几乎可以忽略,但在数据量比较大的时候就会产生一定的消耗。相比之下,React的变动检查则是DOM结构层面的,即使是全新的数据,只要渲染结果没有变化,也不需要重新绘制。

Angular和Vue都提供了重绘的优化机制,即有效地复用实例和DOM元素。在优化的版本中,Angular和Vue采用了track by $index技术后比React的效率更高。

所以在框架选择和技术性能分析的时候,要分清楚初始渲染、小量数据更新、大量数据更新这些不同的场合,以及DOM、脏检查MVVM、数据收集MVVM在不同场合各自的表现和优缺点,具体表现和区别如下。
· 初始渲染阶段:Virtual DOM >脏检查≥依赖收集
· 小量数据更新时:依赖收集>Virtual DOM +优化>脏检查(无法优化)> Virtual DOM无优化
· 大量数据更新时:脏检查+优化≥依赖收集+优化>Virtual DOM(无优化)> MVVM无优化

实际应用

例如在浏览器端使用MVVM , 在服务器端可以使用MVC。而从宏观来看,前端又可视为view,后端又可视为model。