摘自 : https://juejin.im/post/593021272f301e0058273468
MVC
- MVC是一種設(shè)計(jì)模式,它將應(yīng)用劃分為3個(gè)部分:數(shù)據(jù)(M模型)、展示層(V視圖)和用戶交互層V。結(jié)合一下下圖落萎,更能理解三者之間的關(guān)系炬灭。
一個(gè)事件的發(fā)生是這樣的過程
- 用戶和應(yīng)用交互
- 控制器的事件處理器被觸發(fā)
- 控制器從模型中請(qǐng)求數(shù)據(jù)荣赶,并將其交給視圖
- 視圖將數(shù)據(jù)呈現(xiàn)給用戶
模型:用來存放應(yīng)用的所有數(shù)據(jù)對(duì)象训唱。模型不必知曉視圖和控制器的細(xì)節(jié),模型只需包含數(shù)據(jù)及直接和這些數(shù)據(jù)相關(guān)的邏輯凿滤。任何事件處理代碼传泊、視圖模版,以及那些和模型無關(guān)的邏輯都應(yīng)當(dāng)隔離在模型之外
視圖:視圖層是呈現(xiàn)給用戶的鸭巴,用戶與之產(chǎn)生交互眷细。在javaScript應(yīng)用中,視圖大都是由html鹃祖、css和JavaScript模版組成的溪椎。除了模版中簡(jiǎn)單的條件語句之外,視圖不應(yīng)當(dāng)包含任何其他邏輯。事實(shí)上和模型類似校读,視圖也應(yīng)該從應(yīng)用的其他部分中解耦出來
控制器:控制器是模型和視圖的紐帶沼侣。控制器從視圖獲得事件和輸入歉秫,對(duì)它們進(jìn)行處理蛾洛,并相應(yīng)地更新視圖。當(dāng)頁面加載時(shí)雁芙,控制器會(huì)給視圖添加事件監(jiān)聽轧膘,比如監(jiān)聽表單提交和按鈕單擊。然后當(dāng)用戶和應(yīng)用產(chǎn)生交互時(shí)兔甘,控制器中的事件觸發(fā)器就開始工作谎碍。例如JavaScript框架早期框架backbone就是采用的MVC模式
Modal
MVC中M表示model,
Model層用來存儲(chǔ)業(yè)務(wù)的數(shù)據(jù),一旦數(shù)據(jù)發(fā)生變化洞焙,模型將通知有關(guān)的視圖蟆淀。
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上注冊(cè)澡匪,進(jìn)而觀察Model熔任,以便更新在Model上發(fā)生改變的數(shù)據(jù)。
View
view和controller
之間使用了策略模式唁情,這里View引入了Controller的實(shí)例來實(shí)現(xiàn)特定的響應(yīng)策略疑苔,比如這個(gè)栗子中按鈕的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將響應(yīng)機(jī)制封裝在controller對(duì)象中荠瘪,當(dāng)用戶和你的應(yīng)用產(chǎn)生交互時(shí),控制器中的事件觸發(fā)器就開始工作了赛惩。
myapp.Controller = function() {
var model = null,
view = null;
this.init = function() {
/* 初始化Model和View */
model = new myapp.Model();
view = new myapp.View(this);
/* View向Model注冊(cè)哀墓,當(dāng)Model更新就會(huì)去通知View啦 */
model.register(view);
model.notify();
};
/* 讓Model更新數(shù)值并通知View更新視圖 */
this.increase = function() {
model.add(1);
model.notify();
};
this.decrease = function() {
model.sub(1);
model.notify();
};
}
實(shí)例化View并向?qū)?yīng)的Model實(shí)例注冊(cè),當(dāng)Model發(fā)生變化時(shí)就去通知View做更新喷兼,這里用到了觀察者模式篮绰。
當(dāng)我們執(zhí)行應(yīng)用的時(shí)候,使用Controller做初始化:
(function() {
var controller = new myapp.Controller();
controller.init();
})();
MVVM
MVVM(Model-View-ViewModel)
最早由微軟提出季惯。ViewModel
指 "Model of View"
——視圖的模型吠各。
來看一個(gè)Vue的例子:
Model
在MVVM
中勉抓,我們可以把Model稱為數(shù)據(jù)層
贾漏,因?yàn)樗鼉H僅關(guān)注數(shù)據(jù)本身,不關(guān)心任何行為(格式化數(shù)據(jù)由View的負(fù)責(zé))藕筋,這里可以把它理解為一個(gè)類似json的數(shù)據(jù)
對(duì)象纵散。
var data = {
val : 0
}
View
和MVC
不同的是,MVVM
中的View
通過使用模板語法
來聲明式的將數(shù)據(jù)渲染進(jìn)DOM,當(dāng)ViewModel
對(duì)Model進(jìn)行更新的時(shí)候伍掀,會(huì)通過數(shù)據(jù)綁定更新到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了,也是整個(gè)模式的重點(diǎn)蜜笤,業(yè)務(wù)邏輯也主要集中在這里濒蒋,其中的一大核心就是數(shù)據(jù)綁定,后面將會(huì)講到把兔。與MVP不同的是沪伙,沒有了View為Presente提供的接口,之前由Presenter負(fù)責(zé)的View和Model之間的數(shù)據(jù)同步交給了ViewModel中的數(shù)據(jù)綁定進(jìn)行處理垛贤,當(dāng)Model發(fā)生變化焰坪,ViewModel就會(huì)自動(dòng)更新;ViewModel變化聘惦,Model也會(huì)更新某饰。
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精簡(jiǎn)了很多善绎,不僅僅簡(jiǎn)化了業(yè)務(wù)與界面的依賴黔漂,還解決了數(shù)據(jù)頻繁更新(以前用jQuery操作DOM很繁瑣)的問題。因?yàn)樵贛VVM中禀酱,View不知道Model的存在炬守,ViewModel和Model也察覺不到View,這種低耦合模式可以使開發(fā)過程更加容易剂跟,提高應(yīng)用的可重用性减途。
數(shù)據(jù)綁定
雙向數(shù)據(jù)綁定,可以簡(jiǎn)單而不恰當(dāng)?shù)乩斫鉃橐粋€(gè)模版引擎曹洽,但是會(huì)根據(jù)數(shù)據(jù)變更實(shí)時(shí)渲染鳍置。
不同的MVVM框架中,實(shí)現(xiàn)雙向數(shù)據(jù)綁定的技術(shù)有所不同送淆。目前一些主流的前端框架實(shí)現(xiàn)數(shù)據(jù)綁定的方式大致有以下幾種:
- 數(shù)據(jù)劫持 (Vue)
- 發(fā)布-訂閱模式 (Knockout税产、Backbone)
- 臟值檢查 (Angular)
Vue
采用數(shù)據(jù)劫持&發(fā)布-訂閱模式
的方式,通過ES5提供的 Object.defineProperty()
方法來劫持(監(jiān)控)各屬性的 getter 偷崩、setter 辟拷,
并在數(shù)據(jù)(對(duì)象)發(fā)生變動(dòng)時(shí)通知訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)阐斜。并且衫冻,由于是在不同的數(shù)據(jù)上觸發(fā)同步,可以精確的將變更發(fā)送給綁定的視圖谒出,而不是對(duì)所有的數(shù)據(jù)都執(zhí)行一次檢測(cè)羽杰。要實(shí)現(xiàn)Vue中的雙向數(shù)據(jù)綁定渡紫,大致可以劃分三個(gè)模塊:Observer、Compile考赛、Watcher
惕澎,如圖:
- Observer 數(shù)據(jù)監(jiān)聽器
負(fù)責(zé)對(duì)數(shù)據(jù)對(duì)象的所有屬性進(jìn)行監(jiān)聽(數(shù)據(jù)劫持),監(jiān)聽到數(shù)據(jù)發(fā)生變化后通知訂閱者颜骤。 - Compiler 指令解析器
掃描模板唧喉,并對(duì)指令進(jìn)行解析,然后綁定指定事件忍抽。 - Watcher 訂閱者
關(guān)聯(lián)Observer和Compile八孝,能夠訂閱并收到屬性變動(dòng)的通知,執(zhí)行指令綁定的相應(yīng)操作鸠项,更新視圖干跛。Update()是它自身的一個(gè)方法,用于執(zhí)行Compile中綁定的回調(diào)祟绊,更新視圖楼入。
總結(jié)
MV*的目的是把應(yīng)用程序的數(shù)據(jù)、業(yè)務(wù)邏輯和界面這三塊解耦牧抽,分離關(guān)注點(diǎn)嘉熊,不僅利于團(tuán)隊(duì)協(xié)作和測(cè)試,更有利于甩鍋維護(hù)和管理扬舒。業(yè)務(wù)邏輯不再關(guān)心底層數(shù)據(jù)的讀寫阐肤,而這些數(shù)據(jù)又以對(duì)象的形式呈現(xiàn)給業(yè)務(wù)邏輯層。從 MVC --> MVP --> MVVM讲坎,就像一個(gè)打怪升級(jí)的過程孕惜,它們都是在MVC的基礎(chǔ)上隨著時(shí)代和應(yīng)用環(huán)境的發(fā)展衍變而來的。