30行代碼實現Javascript中的MVC
很多講解MVC的例子都從一個具體的框架的某個概念入手,比如Backbone的collection或AngularJS中model,這當然不失為一個好辦法。但框架之所以是框架,而不是類庫(jQuery)或者工具集(Underscore),就是因為它們的背後有着眾多優秀的設計理念和最佳實踐,這些設計精髓相輔相成,環環相扣,缺一不可,要想在短時間內透過複雜的框架而看到某一種設計模式的.本質並非是一件容易的事。
這便是這篇隨筆的由來——為了幫助大家理解概念而生的原型代碼,應該越簡單越好,簡單到剛剛足以大家理解這個概念就夠了。
的基礎是觀察者模式,這是實現model和view同步的關鍵
為了簡單起見,每個model實例中只包含一個primitive value值。
function Model(value) {
this._value = typeof value === 'undefined' ? '' : value;
this._listeners = [];
}
= function (value) {
var self = this;
self._value = value;
// model中的值改變時,應通知註冊過的回調函數
// 按照Javascript事件處理的一般機制,我們異步地調用回調函數
// 如果覺得setTimeout影響性能,也可以採用requestAnimationFrame
setTimeout(function () {
self._ach(function (listener) {
(self, value);
});
});
};
h = function (listener) {
// 註冊監聽的回調函數
this._(listener);
};
// html代碼:
<div id="div1"></div>
// 邏輯代碼:
(function () {
var model = new Model();
var div1 = lementById('div1');
h(function (value) {
rHTML = value;
});
('hello, this is a div');
})();
藉助觀察者模式,我們已經實現了在調用model的set方法改變其值的時候,模板也同步更新,但這樣的實現卻很彆扭,因為我們需要手動監聽model值的改變(通過watch方法)並傳入一個回調函數,有沒有辦法讓view(一個或多個dom node)和model更簡單的綁定呢?
2. 實現bind方法,綁定model和view
= function (node) {
// 將watch的邏輯和通用的回調函數放到這裏
h(function (value) {
rHTML = value;
});
};
// html代碼:
<div id="div1"></div>
<div id="div2"></div>
// 邏輯代碼:
(function () {
var model = new Model();
(lementById('div1'));
(lementById('div2'));
('this is a div');
})();
通過一個簡單的封裝,view和model之間的綁定已經初見雛形,即使需要在一個model上綁定多個view,實現起來也很輕鬆。注意bind是Function類prototype上的一個原生方法,不過它和MVC的關係並不緊密,筆者又實在太喜歡bind這個單詞,一語中的,言簡意賅,所以索性在這裏把原生方法覆蓋了,大家可以忽略。言歸正傳,雖然綁定的複雜度降低了,這一步依然要依賴我們手動完成,有沒有可能把綁定的邏輯從業務代碼中徹底解耦呢?
3. 實現controller,將綁定從邏輯代碼中解耦
細心的朋友可能已經注意到,雖然講的是MVC,但是上文中卻只出現了Model類,View類不出現可以理解,畢竟HTML就是現成的View(事實上本文中從始至終也只是利用HTML作為View,javascript代碼中並沒有出現過View類),那Controller類為何也隱身了呢?別急,其實所謂的”邏輯代碼”就是一個框架邏輯(姑且將本文的原型玩具稱之為框架)和業務邏輯耦合度很高的代碼段,現在我們就來將它分解一下。
如果要將綁定的邏輯交給框架完成,那麼就需要告訴框架如何來完成綁定。由於JS中較難完成annotation(註解),我們可以在view中做這層標記——使用html的標籤屬性就是一個簡單有效的辦法。
function Controller(callback) {
var models = {};
// 找到所有有bind屬性的元素
var views = ySelectorAll('[bind]');
// 將views處理為普通數組
views = (views, 0);
ach(function (view) {
var modelName = ttribute('bind');
// 取出或新建該元素所綁定的model
models[modelName] = models[modelName] || new Model();
// 完成該元素和指定model的綁定
models[modelName](view);
});
// 調用controller的具體邏輯,將models傳入,方便業務處理
(this, models);
}
// html:
<div id="div1" bind="model1"></div>
<div id="div2" bind="model1"></div>
// 邏輯代碼:
new Controller(function (models) {
var model1 = l1;
('this is a div');
});
就這麼簡單嗎?就這麼簡單:在Controller中完成業務邏輯並對Model進行修改,Model的變化觸發View的自動更新,怎麼樣,算得上一個有模有樣的MVC吧?當然,這樣的”框架”還不足以用於生產環境,不過如果它能或多或少地幫助到大家對於MVC的理解的話,博主就非常滿足了。
整理後去掉註釋的”框架”代碼:
function Model(value) {
this._value = typeof value === 'undefined' ? '' : value;
this._listeners = [];
}
= function (value) {
var self = this;
self._value = value;
setTimeout(function () {
self._ach(function (listener) {
(self, value);
});
});
};
h = function (listener) {
this._(listener);
};
= function (node) {
h(function (value) {
rHTML = value;
});
};
function Controller(callback) {
var models = {};
var views = (ySelectorAll('[bind]'), 0);
ach(function (view) {
var modelName = ttribute('bind');
(models[modelName] = models[modelName] || new Model())(view);
});
(this, models);
}
4. 一個簡單的例子
下面請大家看一個簡單例子,如何實現電子錶
// html:
<span bind="hour"></span> : <span bind="minute"></span> : <span bind="second"></span>
// controller:
new Controller(function (models) {
function setTime() {
var date = new Date();
(ours());
(inutes());
(econds());
}
setTime();
setInterval(setTime, 1000);
});
可以看出,controller中只負責更新model的邏輯,和view完全解耦;而view和model的綁定是通過view中的屬性和框架中controller的初始化代碼完成的,也沒有出現在業務邏輯中;至於view的更新,也是通過框架中的觀察者模式實現的。
-
JavaScript 函數表達式
JavaScript中創建函數主要有兩種方法:函數聲明和函數表達式。這兩種方式都有不同的適用場景。這篇筆記主要關注的是函數表達式的幾大特點以及它的使用場景,下面一一描述。主要特點可選的函數名稱函數名稱是函數聲明的必需組成部分,這個函數名稱相當於一個變量,新定...
-
HTML5教程:畫布Canvas基礎知識講解
Canvas是HTML5最讓人期待的特性之一,目前已獲得大部分Web瀏覽器支持Canvas可以幫助創建遊戲、增強圖形用户界面。下面YJBYS小編為大家帶來畫布Canvas基礎知識,希望對大家學習有所幫助!HTML5規範引進了很多新特性,其中最令人期待的之一就是Canvas元素。HTML5Canvas...
-
有關JavaScript中的prototype.bind()方法介紹
以前,你可能會直接設置self=this或者that=this等等,這樣做當然也能起作用,但是使用()會更好,看上去也更專業。下面舉個簡單的例子:複製代碼代碼如下:varmyObj={specialFunction:function(){},anotherSpecialFunction:function(){},getAsyncData:function(cb){cb();}...
-
網頁設計的佈局
網頁設計的工作目標,是通過使用更合理的顏色、字體、圖片、樣式進行頁面設計美化,在功能限定的情況下,儘可能給予用户完美的視覺體驗。以下是小編為您帶來的網頁設計的佈局,看看吧!網頁設計的佈局11、響應式網頁設計響應式網頁設計是網頁設計的一種技術,可在N多種瀏...