MVC/MVVM都是常见的软件架构设计模式,它们是一种改进代码的组织方式,并不是一种设计模式,一个架构模式可能包含了多种设计模式。
Model & View
上面显示数值,两个按钮可以对数值进行加减操作,操作后的数值会更新显示。
Model
Model层用于封装业务逻辑相关的数据以及处理数据的方法。将用到的方法封装到Model中,并且定义了三个不同的操作方法。
1 | var myapp = {} |
View
View作为视图层,主要负责数据的展示。
1 | myapp.View = function() { |
通过Model&View完成了数据从模型层到视图层的逻辑。但是作为一个程序,还需要响应用户的操作,同步更新View和Model。所以MVC中引入了controller,它来定义界面对用户输入的响应方式,连接模型和视图。
MVC
实现代表方法调用,虚线代表事件通知。
用户对View的操作由Controller处理,在Controller中响应View的事件调用Model的接口对数据进行操作,当Model发生变化时,通知相关视图进行更新。
Model
1 | myapp.Model = function() { |
Model和View之间使用了观察者模式,View事先在Model上注册,进而观察Model,以便更新在Model上发生改变的数据。
View
View引入了Controller的实例来实现特定的响应策略,比如按钮中的click
事件:1
2
3
4
5
6
7
8
9
10
11
12
13
14myapp.View = function(controller) {
var $num = $('#num'),
$incBtn = $('#increase'),
$decBtn = $('#decrease');
/* 将model数据更新到视图 */
this.render = function(model) {
$num.text(model.getVal() + 'rmb')
}
/* 绑定事件 */
$incBtn.click(controller.increase)
$decBtn.click(controller.decrease)
}
Controller
控制器是模型和视图之间的纽带,MVC将响应机制封装在Controller对象中,当用户和应用产生交互时,控制器中的事件触发器就开始工作了。
1 | myapp.Controller = function() { |
这一部分使用了观察者模式,实例化View并向对应的Model实例注册,当Model发生变化时,就通知View更新。
当执行应用时,使用Controller初始化:1
2
3
4(function() {
var controller = new myapp.Controller()
controller.init()
})()
MVC的业务逻辑主要在Controller中,而View层也有独立处理用户事件的能力,当每个事件流经Controller时,这层会变得臃肿。并且MVC中View和Controller一般是一一对应的。视图和控制器之间的联系过于紧密,Controller的复用性成了问题。
MVVM
MVVM(Model-View-ViewModel),ViewModel指的是“Model of View”,视图的模型。
MVVM把View和Model的同步逻辑自动化,不再需要手动操作,而是交给数据绑定功能负责,只需要告诉它View显示的数据对应的是Model的哪个部分。
Model
在Model中只关注数据本身,没有其他任何行为(格式化数据由View完成)
1 | var data = { |
View
MVVM中的View通过模板语法来声明式的将数据渲染进DOM,当ViewModel对Model进行更新时,会通过数据绑定更新到View。
1 | <div id="app"> |
ViewModel
ViewModel相当于MVC的Controller。也就是业务逻辑。双向绑定可以使View和Model之间互相响应变化。
1 | new Vue({ |
这样的模式可以将View和Model之间的耦合度降低,业务逻辑能从View层中剥离。
数据绑定
Vue运用了双向绑定技术,也就是ES5的Object.defineProperty
。目前几种MVVM框架中实现数据绑定有这几种方法:
- 数据劫持(Vue)
- 发布订阅模式(Knockout、Backbone)
- 脏值检测(Angular)
用一张网上比较流行的图来解释说明Vue的基本原理:
实现Vue的双向数据绑定分为三个模块:Observe、Compile、Watcher。Vue采用了发布订阅者的架构模式,运用ES5的Object.defineProperty
劫持各属性的getter
、setter
,并在数据发生变动时通知订阅者,触发相应的监听回调。
Observe 数据监听
负责对数据对象所有属性进行劫持,监听到数据变化后通知订阅者。Compile 解析指令
扫描模版,对指令进行解析,然后绑定事件。Watcher 订阅者
关联Observe
和Compile
,订阅并收到属性变动的通知,执行解析指令上的回调函数。Update
是其自身的方法,用于执行Compile
中绑定的回调,更新视图。
总结
MV*目的都是把数据、业务逻辑、表现层这三部分解耦,分离关注点,有利于测试和维护。业务逻辑不关心底层数据的读写,这些数据以对象的形式呈现给业务逻辑层。
曾经我也很困惑是应该追逐热点,完全使用已经日臻完善的所谓“新技术”,还是固守以前的成果,用已经成熟的知识去提供解决方案。其实这两点并不完全冲突,应该思考业务场景和开发需求,不同需求自然有合适的方案。
我们选择它,使用它,就代表了认可它的思想,相信它能提供开发效率,而不仅仅是因为大家都在追逐它们。