前言
最近工作落實了,是我非常喜歡的無線前端,會接觸很多新東西啦,需要努力才行。因為會用到backbone,所以要學習MVC啦。
MVC(模型-視圖-控制器),這種模式最早被用於構建桌面程序和服務器端的應用程序。
最近幾年由於前后端數據的轉移,前端所占的業務邏輯越來越重,於是這種模式便出現在了我們前端的javascript中了,比如Backbone 然后MVP與MVVM也先后在前端出現,並且各自擁有其擁戴者。
初探MVC
MVC是一種架構設計模式,他通過關注點分離改進程序組織,從而達到優化程序的目的。
MVC強制將業務數據(model)與用戶界面(view)分離,controller用以管理用戶輸入以及一些邏輯。 MVC其實也是經過了一些發展的:
model代表特定於領域的數據,他完全不知道什么是view或者controller,但是當model發生改變時,他會通知他的view(觀察者)
view描述的是model的當前狀態,觀察者模式用於讓view了解model什么時候發生了變化
presentation由view關注,但不只是單個view和controller,屏幕上每個部分或者元素都需要做到view-controller對應
controller在view-controller對中的作用是處理用戶交互(點擊、滑動等動作),為view做決定。
由此我們可以察覺,發布/訂閱(publish/subscribe)(觀察者模式)好像和我們的MVC有一定淵源,於是我們來簡單的看一下觀察者模式。
Publish/Subscribe(發布/訂閱)模式
在javascript中,我們一般使用Publish/Subscribe(發布/訂閱)模式。

Publish/Subscribe模式使用了一個主題/事件通道,這個通道介於希望接收到通知(訂閱者)的對象和激活事件的對象(發布者)之間。
該事件系統允許代碼定義應用程序的特定事件,事件可傳遞參數,參數包含訂閱者需要的值,這樣避免了訂閱者與發布者之間產生依賴。
他允許任何訂閱者執行適當的事件處理程序來注冊和接收發布者發出的通知。
我們這里假設我們已經實現了publish、subscribe、unsubscribe方法,這里來實現一個簡單的mail處理程序:
1 //接收消息數量 2 var mailCounter = 0; 3 //初始化訂閱、名稱是inbox/newMessage 4 5 //呈現消息預覽 6 var subscriber1 = subscribe('inbox/newMessage', function (topic, data) { 7 alert('a new message:', topic); 8 //使用從目標subject傳遞過來的data,呈現消息 9 $('#sender').html(data.sender); 10 $('#content').html(data.body); 11 }); 12 13 //另外一個訂閱者,使用同樣的data,但是干了不同的事情(更新接收消息數量) 14 var subscriber2 = subscribe('inbox/newMessage', function (topic, data) { 15 $('#newMessageCounter').html(mailCounter++); 16 }); 17 18 //發布消息啦! 19 publish('inbox/newMessage', [{ 20 sender: '葉小釵', 21 body: '一起來看霹靂布袋戲吧!!!' 22 }]); 23 24 //可以取消訂閱 25 // unsubscribe(subscriber1); 26 // unsubscribe(subscriber2);
這段代碼的中心是促進松散耦合,通過訂閱另一個對象的特定任務或活動,當任務/活動發生時獲得通知,而不是單個對象直接調用其他對象的方法。
我們使用Publish/Subscribe模式的好處就是分解應用程序,松散耦合的塊,改進代碼管理。
這種模式其實也有一個問題,就相當於領導發命令下面的士兵不一定能完成任務,他缺少一個跟蹤機制,訂閱者和發布者之間會相互無視。
Publish/Subscribe非常適用與javascript生態系統,因為ECMAScript是由事件驅動的,在瀏覽器下尤其如此,DOM將事件作為腳本編程的主要交互API,所以在代碼實現中,無論ECMAscript還是dom都不會提供核心對象或方法來創建自定義事件系統(DOM3除外)。
但是很多類庫幫我們實現了Publish/Subscribe系統(jquery) $('dom').trigger('channel', data);
javascript實現Publish/Subscribe
1 var pubsub = {}; 2 (function (q) { 3 var topics = {}, 4 subUid = -1; 5 //發布或者廣播事件 6 q.publish = function (topic, args) { 7 if (!topics[topic]) { 8 return false; 9 } 10 var subscribers = topics[topic], 11 len = subscribers ? subscribers.length : 0; 12 while (len--) { 13 subscribers[len].func(topic, args); 14 } 15 return false; 16 }; 17 18 //通過特定的名稱和回調函數訂閱事件,topic/event觸發時執行事件 19 q.subscribe = function (topic, func) { 20 if (!topics[topic]) { 21 topics[topic] = []; 22 } 23 var token = (++subUid).toString(); 24 topics[topic].push({ 25 token: token, 26 func: func 27 }); 28 return token; 29 }; 30 31 q.unsubscribe = function (token) { 32 for (var m in topics) { 33 if (topics[m]) { 34 for (var i = 0, len = topics[m].length; i < len; i++) { 35 if (topics[m][i].token === token) { 36 topics[m].splice(i, 1); 37 return token; 38 } 39 } 40 } 41 } 42 return this; 43 }; 44 })(pubsub); 45 46 //簡單的消息處理程序 47 48 //記錄所有通過訂閱者接收到的主題和數據 49 var messageLogger = function (topics, data) { 50 //do something 51 console.log(topics + ' ' + data); 52 }; 53 54 //訂閱者監聽訂閱的topic,一旦該topic廣播一個通知,訂閱者就調用回調函數 55 var subscription = pubsub.subscribe('inbox/newMessage', messageLogger); 56 57 //發布者發布消息 58 pubsub.publish('inbox/newMessage', '刀狂劍痴葉小釵'); 59 60 pubsub.publish('inbox/newMessage', { 61 id: '葉小釵', 62 name: '刀狂劍痴' 63 });
因為,這個不是今天的重點,我們暫時寫到這吧,回過頭繼續看我們的MVC。
再探MVC
MVC由三個核心組件組成。
model
model管理應用程序的數據,model不涉及用戶界面,也不涉及表示層,他是應用程序可能需要的特殊形式的數據。
當model改變時,他會通知他的觀察者(view),view就會做出相應的改變達到同步的效果。
假如我們有一個javascript圖片庫應用程序,在圖片庫中,一個圖片就是一個model,他便是一個獨特的數據。
這個model可以包含標題,圖像源等信息 { src: '1.jpg', title: '葉小釵', viewed: false }
View
視圖是model的可視化表示,表示當前狀態的篩選視圖,一個視圖通常檢測一個model(模型),並在model更改時進行通知,使view本身能夠更新。
View通常是“愚鈍”的,因為他對應用程序中的View與Controller了解非常有限。
用戶一般與View發生交互,包括讀取編輯model,我們通過操作view來編輯器元數據,更新model的實際任務是在controller上進行的。
我們下面來看一個例子:
我們在view內定義一個render方法,他負責使用javascript模板引擎渲染model的內容,並產生更新操作(還是圖片相關的操作),然后model將render回調作為一個訂閱者加進去,以便model改變時觸發view更新
1 var bulidPhotoView = function (photoModel, photoController) { 2 var base = $('<div/>'), 3 photoEl = $('<div/>'); 4 base.append(photoEl); 5 var render = function () { 6 //__template模板方法並不存在其功能為獲取id標志的html模板與model生成實體html 7 photoEl.html(__template('id', photoModel)); 8 }; 9 photoModel.addSubscriber(render); 10 photoEl.delegate('click', function () { 11 photoController.handleEvent('click', photoModel); 12 }); 13 var show = function () { 14 photoEl.show(); 15 }; 16 var hide = function () { 17 photoEl.hide(); 18 }; 19 return { showView: show, hideView: hide }; 20 }
Controller
controller是model與view之間的中介,當用戶操作view時,controller負責更新model。
View將委托給controller進行操作,當view覺得合適時,view也會委托controller處理model的事件。
這里有點繞,因為我們javascript中的MVC與經典的有點不一樣。
比如backbone的框架,他包含Model模型和View視圖,但是他其實並不包含Controller,其視圖和路由行為與controller有點類似,但是他們都不是controller。
【階段總結】
MVC關注分離,他有利於簡化應用程序功能的模塊化,從而帶來了這些好處:
① 整體維護變得簡單,數據是否改變,什么時候改變我們可以追蹤,不論是model或者controller最終都會體現在view上
② 解耦model與view
③ model與controller代碼的重復被消除了。
結語
我們今天一起學習了MVC,我對這塊的理解還很淺,因為最近會用到backbone,所以感覺自己應該對這塊知識有所掌握。
接下來我們一起來學習下吧,希望這次學習后對MVC的理解能夠加深一點。
