和通常文章不一樣,本文不依賴於任何現有的框架,也不試圖陷入冗長的發展歷史,而是徹底從頭開始,以一個儘量小可是能夠說明問題的案例,以此講清楚MVC這個歷史悠久、變型極多的技術理念。MVC是一種很是普及的,基礎的設計套路,在不一樣的語言社區內都有着大量的應用。理解了MVC,學習接下來的MVVM、MVP等才能成爲可能。html
MVC把一個系統的類分爲三種:Model,View和Controller。它們也遵循着職責分離原則:app
儘管MVC看起來複雜,其實用代碼表達最簡單的MVC是可能的:框架
/** 模擬 Model, View, Controller */
var M = {}, V = {}, C = {};
/** Model 負責數據 */
M.data = "hello world";
/** View 負責輸出 */
V.render = (M) => { alert(M.data); }
/** Controller 搭橋*/
C.handleOnload = () => { V.render(M); }
window.onload = C.handleOnload;
複製代碼
只要是和用戶交互的都是View,因此使用alert,或者直接輸出到console,都是一種View。函數
接下來,我但願用一個完整可是極簡的程序,來驗證MVC如何從一個平常的程序進化而來的。這個一個小型程序只不過是如此:學習
1 this
<div id="app">
<p><span id="count">0</span>
<button id="inc">+</button>
<button id="dec">-</button>
</p>
</div>
<script>
var counter = document.getElementById('count');
var btn1 = document.getElementById('inc');
var btn2 = document.getElementById('dec');
var count = 0;
btn1.addEventListener('click',function (){
counter.innerHTML = ++count;
}
)
btn2.addEventListener('click',function (){
counter.innerHTML = --count;
}
)
</script>
複製代碼
當前的小型程序,全部的代碼,不管數據仍是邏輯仍是UI代碼,都是混合在一塊兒的,並無所謂的任何的職責分離。由於還小,問題不大。可是產品代碼都是從這一的基礎上逐步長大的。好比說數據就不太可能只有一個count,代碼逐步增大,一個對象的數據屬性會愈來愈多,隨着來的是操做數據的函數也會愈來愈多。同理包括用戶界面和業務邏輯。spa
若是使用MVC的眼光來看,在此微觀模式下,其實可使用MVC的模式作代碼的職責分離。其中全部的UI元素對象,都應該放置到View類型內,其中的事件處理都是應該放到Controller內,而數據,也就是這裏的count變量和對它的操做(減一加一),應該放置到Model類內,組裝Model和View則是Controller的職責。這樣的思路下,代碼能夠改爲:prototype
<div id="app">
<p><span id="count">1</span>
<button id="inc">+</button>
<button id="dec">-</button>
</p>
</div>
<script>
class Model{
constructor(){
this.count = 1
}
inc(){
return this.count++
}
dec(){
return this.count--
}
}
class View{
constructor(){
this.counter = document.getElementById('count')
this.btn1 = document.getElementById('inc')
this.btn2 = document.getElementById('dec')
}
setCount(c){
this.counter.innerHTML = c
}
attachInc(cb){
this.btn1.addEventListener('click',cb)
}
attachDec(cb){
this.btn2.addEventListener('click',cb)
}
}
class Controller {
constructor(){
this.m = new Model()
this.v = new View()
this.v.attachInc(this.onInc.bind(this))
this.v.attachDec(this.onDec.bind(this))
}
onInc(){
this.v.setCount(this.m.inc())
}
onDec(){
this.v.setCount(this.m.dec())
}
}
var c = new Controller()
</script>
複製代碼
將應用程序劃分爲三種組件,模型 - 視圖 - 控制器(MVC)設計定義它們之間的相互做用。設計
Model用於封裝數據以及對數據的處理方法。Model不依賴「View」和「Controller」,也就是說, Model 不關心它會被如何顯示或是如何被操做。Model 中數據的變化通常會經過一種刷新機制被公佈。爲此,Model須要提供被監聽的機制。code
View可以實現顯示。在 View 中通常沒有程序上的邏輯。爲了實現Model變化後的響應View 上的刷新功能,View須要監聽Model的變化。
控制器(Controller)起到不一樣層面間的組織做用,用於控制應用程序的流程。它處理事件並做出響應。「事件」包括用戶的行爲和數據 Model 上的改變。
實際上,經過觀察者模式,能夠把Model的修改刷新到多個視圖中,只要視圖作一個Model變化的訂閱便可。能夠先作一個簡單的觀察者代碼:
var Event = function (sender) {
this._sender = sender;
this._listeners = [];
}
Event.prototype = {
attach: function (listener) {
this._listeners.push(listener);
},
notify: function (args) {
for (var i = 0; i < this._listeners.length; i += 1) {
this._listeners[i](this._sender, args);
}
}
};
複製代碼
而後把應用稍做修改,在加上一個span,其中爲第一個span的值乘以2。界面看起來是這樣:
1|2
<div id="app">
<p><span id="count">1</span>|<span id="count2">2</span>
<button id="inc">+</button>
<button id="dec">-</button>
</p>
</div>
<script>
class Model{
constructor(e){
this.e = e
this.count = 1
}
inc(){
this.count++
this.e.notify(this.count)
return this.count
}
dec(){
this.count--
this.e.notify(this.count)
return this.count
}
}
class View{
constructor(e){
this.e = e
this.e.attach(this.f.bind(this))
this.counter = document.getElementById('count')
this.counter2 = document.getElementById('count2')
this.btn1 = document.getElementById('inc')
this.btn2 = document.getElementById('dec')
}
f(sender,c){
this.counter2.innerHTML = c * 2
}
setCount(c){
this.counter.innerHTML = c
}
attachInc(cb){
this.btn1.addEventListener('click',cb)
}
attachDec(cb){
this.btn2.addEventListener('click',cb)
}
}
var Event = function (sender) {
this._sender = sender;
this._listeners = [];
}
Event.prototype = {
attach: function (listener) {
this._listeners.push(listener);
},
notify: function (args) {
for (var i = 0; i < this._listeners.length; i += 1) {
this._listeners[i](this._sender, args);
}
}
};
class Controller {
constructor(){
this.e = new Event()
this.m = new Model(this.e)
this.v = new View(this.e)
this.v.attachInc(this.onInc.bind(this))
this.v.attachDec(this.onDec.bind(this))
}
onInc(){
this.v.setCount(this.m.inc())
}
onDec(){
this.v.setCount(this.m.dec())
}
}
var c = new Controller()
</script>
複製代碼
此應用的最後一個實現,看起來更加具有了一個MVC的多個方面: