摘自 : https://juejin.im/post/593021272f301e0058273468
MVC
- MVC是一种设计模式,它将应用划分为3个部分:数据(M模型)、展示层(V视图)和用户交互层V。结合一下下图,更能理解三者之间的关系。
一个事件的发生是这样的过程
- 用户和应用交互
- 控制器的事件处理器被触发
- 控制器从模型中请求数据,并将其交给视图
- 视图将数据呈现给用户
模型:用来存放应用的所有数据对象。模型不必知晓视图和控制器的细节,模型只需包含数据及直接和这些数据相关的逻辑。任何事件处理代码、视图模版,以及那些和模型无关的逻辑都应当隔离在模型之外
视图:视图层是呈现给用户的,用户与之产生交互。在javaScript应用中,视图大都是由html、css和JavaScript模版组成的。除了模版中简单的条件语句之外,视图不应当包含任何其他逻辑。事实上和模型类似,视图也应该从应用的其他部分中解耦出来
控制器:控制器是模型和视图的纽带。控制器从视图获得事件和输入,对它们进行处理,并相应地更新视图。当页面加载时,控制器会给视图添加事件监听,比如监听表单提交和按钮单击。然后当用户和应用产生交互时,控制器中的事件触发器就开始工作。例如JavaScript框架早期框架backbone就是采用的MVC模式
Modal
MVC中M表示model,
Model层用来存储业务的数据,一旦数据发生变化,模型将通知有关的视图。
myapp.Modal = function(){
var val = 0
this.add = (v) => { if(val < 100) val += v}
this.sub = (v) => { if(val > 0) val -= v}
this.getVal = () => val
//观察者模式
var self = this,
views = [];
this.register = view => {views.push(view)}
this.notify = () => {
for(var i = 0; i< views.length; i++){
views[i].render(self)
}
}
}
Model和View之间使用了观察者模式,View事先在此Model上注册,进而观察Model,以便更新在Model上发生改变的数据。
View
view和controller
之间使用了策略模式,这里View引入了Controller的实例来实现特定的响应策略,比如这个栗子中按钮的click
事件:
myapp.View = function(controller) {
var $num = $('#num'),
$incBtn = $('#increase'),
$decBtn = $('#decrease');
this.render = function(model) {
$num.text(model.getVal() + 'rmb');
};
/* 绑定事件 */
$incBtn.click(controller.increase);
$decBtn.click(controller.decrease);
};
Controller
控制器C
是模型和视图之间的纽带,MVC将响应机制封装在controller对象中,当用户和你的应用产生交互时,控制器中的事件触发器就开始工作了。
myapp.Controller = function() {
var model = null,
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();
};
}
实例化View并向对应的Model实例注册,当Model发生变化时就去通知View做更新,这里用到了观察者模式。
当我们执行应用的时候,使用Controller做初始化:
(function() {
var controller = new myapp.Controller();
controller.init();
})();
MVVM
MVVM(Model-View-ViewModel)
最早由微软提出。ViewModel
指 "Model of View"
——视图的模型。
来看一个Vue的例子:
Model
在MVVM
中,我们可以把Model称为数据层
,因为它仅仅关注数据本身,不关心任何行为(格式化数据由View的负责),这里可以把它理解为一个类似json的数据
对象。
var data = {
val : 0
}
View
和MVC
不同的是,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;
}
}
}
});
整体来看,比MVC精简了很多,不仅仅简化了业务与界面的依赖,还解决了数据频繁更新(以前用jQuery操作DOM很繁琐)的问题。因为在MVVM中,View不知道Model的存在,ViewModel和Model也察觉不到View,这种低耦合模式可以使开发过程更加容易,提高应用的可重用性。
数据绑定
双向数据绑定,可以简单而不恰当地理解为一个模版引擎,但是会根据数据变更实时渲染。
不同的MVVM框架中,实现双向数据绑定的技术有所不同。目前一些主流的前端框架实现数据绑定的方式大致有以下几种:
- 数据劫持 (Vue)
- 发布-订阅模式 (Knockout、Backbone)
- 脏值检查 (Angular)
Vue
采用数据劫持&发布-订阅模式
的方式,通过ES5提供的 Object.defineProperty()
方法来劫持(监控)各属性的 getter 、setter ,
并在数据(对象)发生变动时通知订阅者,触发相应的监听回调。并且,由于是在不同的数据上触发同步,可以精确的将变更发送给绑定的视图,而不是对所有的数据都执行一次检测。要实现Vue中的双向数据绑定,大致可以划分三个模块:Observer、Compile、Watcher
,如图:
- Observer 数据监听器
负责对数据对象的所有属性进行监听(数据劫持),监听到数据发生变化后通知订阅者。 - Compiler 指令解析器
扫描模板,并对指令进行解析,然后绑定指定事件。 - Watcher 订阅者
关联Observer和Compile,能够订阅并收到属性变动的通知,执行指令绑定的相应操作,更新视图。Update()是它自身的一个方法,用于执行Compile中绑定的回调,更新视图。
总结
MV*的目的是把应用程序的数据、业务逻辑和界面这三块解耦,分离关注点,不仅利于团队协作和测试,更有利于甩锅维护和管理。业务逻辑不再关心底层数据的读写,而这些数据又以对象的形式呈现给业务逻辑层。从 MVC --> MVP --> MVVM,就像一个打怪升级的过程,它们都是在MVC的基础上随着时代和应用环境的发展衍变而来的。