在之前的一篇文章中介绍了mvc
与三层架构
的区别,主要是因为大部分人对mvc
的误用。实际上跟mvc
一起对比的应该是mvp
、mvvm
这些表现层(Presentation Layer
)的设计模式。它们的目的都是把显示的 UI 跟绑定的状态(data)拆开而已,更多的应用在前端。
MVC
我们今天谈的 MVC 大都是应用在 web 框架里面,实际上是经典 MVC 的 web 变体。它们之间的通信方式如下:
- View 通过 http 提交数据给服务器的 Controller
- Controller 要求 Model 改变状态
- Model 执行某些操作,再通过 http 响应将结果回送给 View,View(浏览器)接受到数据更新界面
最早期的 MVC(也就是 Smalltalk MVC)是运用于 GUI 的,它的流程如下:
Controller 接收用户的操作,并根据写好的代码进行相应的操作 —— 触发 Model 层,或者触发 View 层,抑或是两者都触发,整个过程就是这样的。View 层中的数据是通过监听 Model 层数据变化而自动更新的,与 Controller 层无关。Controller 层触发 View 层时,并不会更新 View 层中的数据。
整个 MVC 的设计中包含了三个设计模式。
- View层,单独实现了组合模式
- Model层和View层,实现了观察者模式
- View层和Controller层,实现了策咯模式
关于 MVC 我们需要知道的是应用于 GUI 的 MVC 与 web 的 MVC 是不一样的。
经典 MVC 模式有以下缺点:
- 模型需要将状态变化传达给视图
- 视图拥有模型的完整知识,视图和模型之间没有明确的契约,这意味着视图并不像它应该的那样被动。
MVP
Model View Presenter (MVP) 模式是从 MVC 演变而来的,它试图解决上述问题。最初的 MVP 今天也不再使用,根据 Martin Fowler 的说法,我们使用的是Passive View
或Supervising Controller
这两种变体。
Passive View
Passive View
这种变体是大多数人讨论的 MVP,它的流程如下:
MVP 框架中,View 层不能再直接访问 Model 层,必须通过 Presenter 层提供的接口,然后 Presenter 层再去访问 Model 层。这种方式巧妙地将视图与模型分开,打破了它们之间的关系。MVP 的核心是通过Presenter
来与View
和Model
交互。
这种方式存在的缺点是Presenter
负责全部的View
与Model
的同步工作,所有的消息都需要通过Presenter
传递。这样Presenter
显得很重,维护起来会比较困难。而且由于没有数据绑定,如果Presenter
对视图渲染的需求增多,它不得不过多关注特定的视图,一旦视图需求发生改变,Presenter
也需要改动。
Supervising Controller
Supervising Controller
这种变体一般讨论的比较少。 它与Passive View
最大的不同在于 View
会直接与Model
打交道,并且与Model
进行数据绑定。在有的实现中Presenter
的职责还包括就是将Model
数据传递给View
。
Supervising Controller
模式强调Controller
只处理复杂逻辑和部分与View
和Model
之间的同步。Controller
中尽可能减少View
与Model
的同步工作,将其转交数据绑定来实现,Controller
只处理同步机制无法处理的复杂的交互和同步。因此,这种模式中View
与Model
需要直接通信,存在依赖关系。
这两种模式的选择一般取决于可测试性与复杂性之间的取舍。Passive View
由于视图不负责任何逻辑,所以更易于测试。
MVVM
MVVM乍一看好像是MVP
的Presenter
换成了ViewModel
。它与MVP
的唯一区别就是把View
和Model
的同步逻辑自动化了。以前Presenter
负责View
和Model
同步不再手动地进行操作,而是交给框架所提供的数据绑定功能进行负责。
对于MVVM
来说,双向绑定(data-binding) 是它很核心的概念。目前一些前端框架的实现方式,都是以下几种:
- 数据劫持 (Vue)
- 发布-订阅模式 (Knockout、Backbone)
- 脏值检查 (Angular)
另外就是ViewModel
到底是一个什么样的存在,很多人都不太清楚。
首先Model
一般特指包含领域逻辑的模型,负责业务实现。View
是表示ViewModel
状态的交互式 UI
,它包含数据绑定、事件和行为,需要知道ViewModel
。可以理解为Vue
单文件中的<template></template>
。
ViewModel
可以同时和多个Domain Model
交互,它并不是一个特定Domain Model
为了适配View
而构建的外观模式。我们可以认为ViewModel
是一个特殊的,不依赖于系统GUI
框架的View
。此外,需要注意的是。一个ViewModel
可以应用于多个View
,但是一个View
只能拥有一个ViewModel
。所有的事件和响应逻辑都需要ViewModel
来处理,因此它的View
变得很简单。比如在Vue中:一个组件的实例代表一个ViewModel
。
虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示组件实例。
哪些框架实现了MVVM
这个问题一般在一些技术面试中会问到,比如:is React a MVC or MVVM? 另外还有除了angularJS还有哪些MVVM的框架?等等。其实在 MVVM 的维基百科中把基本上实现了双向绑定的框架都归于 MVVM 了。
关于 React 的 MVVM 讨论,有的人认为它只是VVM
或是VC
,加上Redux
等一些状态库才能完整组成MVVM
:MVVM architectural pattern for a ReactJS application。其实这种问题深究下去已经没有什么意义了,很多框架对于MVVM的实现都有着自己不同的理解,就像MVC一样。我们只要知道最核心的问题,以及如何最大化去利用这些框架才是最重要的。做一个优秀的工程师比做这种技术考古价值要大得多。
参考
- Presentation Patterns : MVC, MVP, PM, MVVM
- MVC、MVP、MVVM的区别和联系(精讲版)
- Ruby on Rails MVC is not the same as Ember.js MVC
- 写给前端看的架构文章(1):MVC VS Flux
- Retirement note for Model View Presenter Pattern
- What are MVP-Passive View and MVP-Supervising controller
- 前端架构 101(五):从 Flux 进化到 Model-View-Presenter
- 浅析前端开发中的 MVC/MVP/MVVM 模式