MVC,MVP和MVVM都是常见的软件架构设计模式,它通过分离关注点来改进代码的组织方式。
它们相同的部分是MV(Model-View),而不同部分则是C(Controller)、P(Presenter)、VM(View-Model)。
MVC
Model
Model层用于封装和应用程序的业务逻辑相关的数据以及对数据的处理方法。
View
作为视图层,主要负责数据的展示。
Controller
控制器是模型和视图之间的纽带,MVC将响应机制封装在controller对象中,当用户和你的应用产生交互时,控制器中的事件触发器就开始工作了。
<div id="num"></div>
<button id="decrease">-</button>
<button id="increase">+</button>
<script>
//创建应用对象
var myapp = {}
//Model层
myapp.Model = function () {
//需要操作的数据
var val = 0;
/* 操作数据的方法 */
this.add = function (v) {
if (val < 100) val += v
}
this.sub = function (v) {
if (val > 0) val -= v
}
this.getVal = function () {
return val;
}
/* 观察者模式 */
var self = this;
var views = [];
this.register = function (view) {
views.push(view)
}
this.notify = function () {
for (var i = 0; i < views.length; i++) {
views[i].render(self)
}
}
}
//View层
myapp.View = function (controller) {
//视图元素
var $num = document.getElementById('num')
var $inBtn = document.getElementById('increase')
var $deBtn = document.getElementById('decrease')
//渲染数据
this.render = function (model) {
$num.innerHTML = model.getVal()
}
/* 绑定事件 */
$inBtn.onclick = function () {
controller.increase()
}
$deBtn.onclick = function () {
controller.decrease()
}
}
//Controller层
myapp.Controller = function () {
var model = null;
var view = null;
this.init = function () {
//初始化Model 和 View
model = new myapp.Model()
view = new myapp.View(this)
//View向Model注册,当Model更新就会去通知View
model.register(view)
model.notify()
}
/* 让Model更新数据并通知View更新视图 */
this.increase = function () {
model.add(1)
model.notify()
}
this.decrease = function () {
model.sub(1)
model.notify()
}
}
//运行,使用Controller做初始化
!(function () {
var controller = new myapp.Controller()
controller.init()
})();
</script>
MVC模式的业务逻辑主要集中在Controller层,而View层其实已经具备了独立处理用户事件的能力,当每个事件都流经Controller层时,这层会变得十分臃肿。
而且,View层和Controller层一般是一一对应的,捆绑起来表示一个组件,视图与控制器间的过于紧密的连接让Controller层的复用性成了问题。
MVP
是MVC模式的改良。和MVC的相同之处是:Controller和Presenter负责业务逻辑,Model管理数据,View负责显示。
在MVC里,View是可以直接访问Model的,但MVP中的View不能直接使用Model,而是通过为Presenter提供接口,让Presenter去更新Model,再通过观察者模式更新View。
与MVC相比,MVP模式通过解耦View和Model,完全分离视图和模型使职责划分更加清晰;由于View不依赖Model,可以将View抽离出来做成组件,它只需要提供一系列接口提供给上层操作。
Model
依然是主要与业务相关的数据和对应处理的数据的方法
View
定义了Presenter和View之间的接口,用户对View的操作都转移到了Presenter。
Presenter
Presenter作为View和Model之间的“中间人”,除了基本的业务逻辑外,还有大量代码需要对从View到Model和从Model到View的数据进行“手动同步”,这样Presenter显得很重,维护起来会比较困难。而且由于没有数据绑定,如果Presenter对视图渲染的需求增多,它不得不过多关注特定的视图,一旦视图需求发生改变,Presenter也需要改动。
<div id="num"></div>
<button id="decrease">-</button>
<button id="increase">+</button>
<script>
//创建应用对象
var myapp = {}
//Model层
myapp.Model = function () {
//需要操作的数据
var val = 0;
/* 操作数据的方法 */
this.add = function (v) {
if (val < 100) val += v
}
this.sub = function (v) {
if (val > 0) val -= v
}
this.getVal = function () {
return val;
}
}
//View层
myapp.View = function () {
//视图元素
var $num = document.getElementById('num')
var $inBtn = document.getElementById('increase')
var $deBtn = document.getElementById('decrease')
//渲染数据
this.render = function (model) {
$num.innerHTML = model.getVal()
}
/* 绑定事件 爆露setter接口让Presenter调用*/
this.init = function(){
//创建presenter
var presenter = new myapp.Presenter(this);
$inBtn.onclick = function () {
presenter.increase()
}
$deBtn.onclick = function () {
presenter.decrease()
}
}
}
//Controller层
myapp.Presenter = function (view) {
//初始化Model 和 View
var _model = new myapp.Model();
var _view = view;
//初始化视图更新
_view.render(_model)
/* 让Model更新数据并通知View更新视图 */
this.increase = function () {
_model.add(1)
_view.render(_model)
}
this.decrease = function () {
_model.sub(1)
_view.render(_model)
}
}
//运行,使用Controller做初始化
!(function () {
var view = new myapp.View()
view.init()
})();
</script>
MVVM
MVVM把View和Model的同步逻辑自动化了。以前Presenter负责的View和Model同步不再手动地进行操作,而是交给框架所提供的数据绑定功能进行负责,只需要告诉它View显示的数据对应的是Model哪一部分即可。以Vue举例。
Model
在MVVM中,我们可以把Model称为数据层,因为它仅仅关注数据本身,不关心任何行为(格式化数据由View的负责),这里可以把它理解为一个类似json的数据对象。
var data = { val: 0 };
View
MVVM中的View通过使用模板语法来声明式的将数据渲染进DOM,当ViewModel对Model进行更新的时候,会通过数据绑定更新到View。
<div id="myapp">
<div>
<span>{{ val }}rmb</span>
</div>
<div>
<button v-on:click="sub(1)">-</button>
<button v-on:click="add(1)">+</button>
</div>
</div>
ViewModel
ViewModel大致上就是MVC的Controller和MVP的Presenter了,也是整个模式的重点,业务逻辑也主要集中在这里,其中的一大核心就是数据绑定,与MVP不同的是,没有了View为Presente提供的接口,之前由Presenter负责的View和Model之间的数据同步交给了ViewModel中的数据绑定进行处理,当Model发生变化,ViewModel就会自动更新;ViewModel变化,Model也会更新。
new Vue({ el: '#myapp', data: data, methods: { add(v) { if(this.val < 100) { this.val += v; } }, sub(v) { if(this.val > 0) { this.val -= v; } } } });
不同的MVVM框架中,实现双向数据绑定的技术有所不同。目前一些主流的前端框架实现数据绑定的方式大致有以下几种:
- 数据劫持 (Vue)
- 发布-订阅模式 (Knockout、Backbone)
- 脏值检查 (Angular)