# MVVM
MVVM(Model View ViewModel)是一種基于MVC和MVP的架構模式,它試圖將用戶界面(UI)從業務邏輯和行為中更加清晰地分離出來。為了這個目的,很多例子使用聲明變量綁定來把View層的工作從其他層分離出來。
這促進了UI和開發工作在同一代碼庫中的同步進行。UI開發者用他們的文檔標記(HTML)綁定到ViewModel,在這個地方Model和ViewModel由負責邏輯的開發人員維護。
## 歷史
MVVM(如其大名)最初是由微軟定義,用于Windows Presentation Foundation(WPF)和Silverlight,在John Grossman2005年的一篇關于Avalon(WPF的代號)的博文中被官方推出。它也作為方便使用MVC的一種可選方案,為Adobe Flex社區積累了一些用戶量。
先于微軟采用的MVVM名稱,在社區中已經有了一場由MVC像MVPM遷移的運動:模型-視圖-展現模型。Marton Fowler在2004年為那些對此感興趣的人寫了一篇關于展現模型的文章。展現模型的理念的內容要遠遠長于這篇文章,然而這篇文章被認為是這一理念的重大突破,并且極大的捧紅了它。
在微軟推出作為MVPM的可選方案的MVVM后,就出現了許多沸沸揚揚的“alt.net”圈子。其中許多聲稱這個公司在GUI世界的霸主地位給與了它們將社區統一為整體的機會,出于市場營銷的目的,按照它們所高興的方式對已有的概念重新命名。一個進步的群體也承認MVVM和MVPM其實實在是同樣的概念,只是展現出來的是不同的包而已。
在近幾年,MVVM已經在Javascript中得到了實現,其構造框架的形式諸如KnockoutJS,Kendo MVVM和Knockback.js,獲得了整個社區的積極響應。
現在就讓我們來看看組成了MVVM的這三個組件。
## 模型
和其它MV*家族成員一樣,MVVM中的模型代表我們的應用用到的領域相關的數據或者信息。一個領域相關的數據的典型例子是用戶賬號(例如名字,頭像,電子郵件)或者音樂唱片(例如唱片名,年代,專輯)。
模型持有信息,但是通常沒有操作行為。它們不會格式化信息,也不會影響數據在瀏覽器中的表現,因為這些不是模型的責任。相反,數據格式化是由視圖層處理的,盡管這種行為被認為是業務邏輯,這個邏輯應該被另外一個層封裝,這個層和模型交互,這個曾就是視圖模型。
這個規則唯一的例外是驗證,由模型進行數據驗證是被認為可以接受的,這些數據用于定義或者更新現存的模型(例如輸入的電子郵件地址是否滿足特定的正則表達式要求?)。
在KnockoutJS中,模型遵從上面的定義,但是通常對服務端服務的Ajax調用被做成即可以讀取也可以寫入模型數據。
如果我們正在構建一個簡單的Todo應用,使用KnockoutJS模型來表示一個Todo條目,看起來像下面這個樣子:
~~~
var Todo = function ( content, done ) {
this.content = ko.observable(content);
this.done = ko.observable(done);
this.editing = ko.observable(false);
};
~~~
> 注意:在上面小段代碼里面,你可能發現了,我們在KnockoutJS的名字空間里面調用observable()方法。在KnockoutJS中,觀察者是一類特殊的JavaScript對象,可以將變化通知給訂閱者,并且自動檢測依賴關系。這個特性使我們在模型值修改之后,可以同步模型和視圖模型。
## 視圖
使用MVC,視圖是應用程序中用戶真正與之打交道的唯一一個部分.它們是展現一個視圖模型狀態的一個可交互UI.此種意義而言,視圖是主動的而不是被動的,而這也是真正的MVC和MVP的觀點.在MVC,MVP和MVVM中視圖也可以是被動的,而這又是什么意思呢?
被動視圖僅僅只輸出要展示的東西,而不去接受任何用戶的輸入。
這樣一個視圖在我們的應用程序中可能也沒有真正的模型的概念,而可以被一個代理控制.MVVM的主動視圖包含數據綁定,事件和需要能夠理解視圖模型的行為.盡管這些行為能夠被映射到屬性,視圖仍然處理這來自視圖模型的事件。
記住視圖在這里并不負責處理狀態時很重要的——它使得其與視圖模型得以同步。
KnockoutJS視圖是簡單的一個帶有聲明鏈接到視圖模型的HTML文檔。KnockoutJS視圖展示來自視圖模型的信息,并且傳遞命令給他(比如,用戶在一個元素上面點擊),并且針對視圖模型的變化更新狀態。而使用來自視圖模型的數據來生成標記的模板也能夠被用在這個目的上。
未來給出一個簡單的初始示例,我們可以看看Javascritpt的MVVM框架KnockoutJS,看它如何允許一個視圖模型的定義,還有它在標記中的相關綁定。
視圖模型:
~~~
var aViewModel = {
contactName: ko.observable("John")
};
ko.applyBindings(aViewModel);
~~~
視圖:
~~~
<p><input id="source" data-bind="value: contactName, valueUpdate: 'keyup'" /></p>
<div data-bind="visible: contactName().length > 10">
You have a really long name!
</div>
<p>Contact name: <strong data-bind="text: contactName"></strong></p>
~~~
我們的text-box輸入(源)從contactName獲取它的初始值,無論何時contactName發生了改變都會自動更新這個值.由于數據綁定是雙向的,像text-box中輸入也將據此更新contactName,以此保持值總是同步的。
盡管這個實現特定于KnockoutJS,但是包含著"You have a really long name!"文本的
標簽包含有簡單的驗證(同樣是以數據綁定的形式呈現)。如果輸入超過10個字符,這個標簽就會顯示,否則保持隱藏。
讓我們看看一個更高級的例子,我們可以看看我們的Todo應用。一個用于這個應用的裁剪后的KnockoutJS的視圖,包含有所有必要的數據綁定,這個視圖看起來是下面這個樣子。
~~~
<div id="todoapp">
<header>
<h1>Todos</h1>
<input id="new-todo" type="text" data-bind="value: current, valueUpdate: 'afterkeydown', enterKey: add"
placeholder="What needs to be done?"/>
</header>
<section id="main" data-bind="block: todos().length">
<input id="toggle-all" type="checkbox" data-bind="checked: allCompleted">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-bind="foreach: todos">
<!-- item -->
<li data-bind="css: { done: done, editing: editing }">
<div class="view" data-bind="event: { dblclick: $root.editItem }">
<input class="toggle" type="checkbox" data-bind="checked: done">
<label data-bind="text: content"></label>
<a class="destroy" href="#" data-bind="click: $root.remove"></a>
</div>
<input class="edit' type="text"
data-bind="value: content, valueUpdate: 'afterkeydown', enterKey: $root.stopEditing, selectAndFocus: editing, event: { blur: $root.stopEditing }"/>
</li>
</ul>
</section>
</div>
~~~
請注意,這個標記的基本布局是相對直觀的,包含有一個輸入文本框(新的todo)用于增加新條目,用于標記條目完成的開關,以及一個擁有模板的列表(todo列表),這個模板以anli的形式展現Todo條目。
上面標記中綁定的數據可以分成下面幾塊:
* 輸入的文本框new-todo 有一個當前屬性的數據綁定,當前要增加的條目的值存儲在這里。我們的視圖模型(后面就會看到)觀察當前屬性,并且綁定在添加事件上。當回車鍵按下的時候,添加事件就被出發了,我們的視圖模型就可以處理當前的值按照需要并且將其加入到Todo列表中。
* 輸入勾選框可以通過點擊標示所有當前條目為完成狀態。如果勾選了,觸發完成事件,這個事件可以被模型視圖觀察到。
* 有一類條目是進行中狀態。當一個任務被標記為進行中,CSS類也會根據這個狀態進行標識。如果雙擊條目,$root.editItem 回調就會被執行。
* toggle類的勾選框表明當前的進行狀態。
* 一個文本標簽包含有Todo條目的內容
* 當點擊一個移除按鈕時可以調用$root.remove 回調函數。
* 編輯模式下的一個輸入文本框可以保存Todo條目的內容。回車鍵事件將會設定編輯屬性為真或者假。
## 視圖模型
視圖模型被認為是一個專門進行數據轉換的控制器。它可以把對象信息轉換到視圖信息,將命令從視圖攜帶到對象。
例如,我們想象我們有一個對象的日期屬性是unix格式的(e.g 1333832407),而不是用戶視圖的所需要的日期格式(e.g 04/07/2012 @ 5:00pm),這時就有必要把unix的日期格式轉換為視圖需要的格式。我們的對象只簡單保存原始的unix數據格式日期,視圖模型作為一個中間人角色會格式化原始的unix數據格式轉換為視圖需要的日期格式。
在這個場景下,視圖模型可以被看做一個對象,它處理很多視圖顯示邏輯。視圖模型也對外提供更新視圖狀態的方法,并通過視圖方法和觸發事件更新對象。
簡單來說,視圖模型位于我們UI層后面層。它通過視圖發布對象的公共數據,同時它作為視圖源提供數據和方法。
KnockoutJS描述視圖模型作為數據的表現和操作可以在UI上訪問和執行。視圖模型并不是一個UI對象,也不是數據持久化對象,而是一個能夠為用戶提供儲存狀態及操作的層次對象。Knockout的視圖模型實現了JavaScript對象與HTML語言無關性。通過這個實現使開發保持了簡單,意味著我們可以在視圖層更加簡單的管理更多的組合方法。
對于我們的ToDo應用程序的一部分KnockoutJS視圖模型可以是像下面這樣:
~~~
// our main ViewModel
var ViewModel = function ( todos ) {
var self = this;
// map array of passed in todos to an observableArray of Todo objects
self.todos = ko.observableArray(
ko.utils.arrayMap( todos, function ( todo ) {
return new Todo( todo.content, todo.done );
}));
// store the new todo value being entered
self.current = ko.observable();
// add a new todo, when enter key is pressed
self.add = function ( data, event ) {
var newTodo, current = self.current().trim();
if ( current ) {
newTodo = new Todo( current );
self.todos.push( newTodo );
self.current("");
}
};
// remove a single todo
self.remove = function ( todo ) {
self.todos.remove( todo );
};
// remove all completed todos
self.removeCompleted = function () {
self.todos.remove(function (todo) {
return todo.done();
});
};
// writeable computed observable to handle marking all complete/incomplete
self.allCompleted = ko.computed({
// always return true/false based on the done flag of all todos
read:function () {
return !self.remainingCount();
},
// set all todos to the written value (true/false)
write:function ( newValue ) {
ko.utils.arrayForEach( self.todos(), function ( todo ) {
//set even if value is the same, as subscribers are not notified in that case
todo.done( newValue );
});
}
});
// edit an item
self.editItem = function( item ) {
item.editing( true );
};
..
~~~
上面我們基本上提供了必需的加入、編輯或者移除記錄的方法,還有標記所有現存的記錄已經被完成的邏輯。注意:唯一真正需要關注的同前面我們的視圖模型的示例的不同之處就是觀察數組.在KnockoutJS中,如果我們希望監測到并且去回應一個單獨的對象發生的改變,我們可以使用觀察.然而如果我們希望檢測并且去回應一個集合的事物所發生的改變,我們可以換用一個觀察數組.如何使用觀察數組的一個簡單示例就像下面這樣:
~~~
// Define an initially an empty array
var myObservableArray = ko.observableArray();
// Add a value to the array and notify our observers
myObservableArray.push( 'A new todo item' );
~~~
> 注意:感興趣的話,我們在前面所提到的完整的KnockoutJS Todo應用程序可以從 TodoMVC 獲取到。
### 扼要重述: 視圖和視圖模型
視圖和視圖模型使用數據綁定和事件進行通信。正如我們之前的視圖模型例子所見,視圖模型不僅僅發布對象屬性,它還提供其他的方法和特性,諸如驗證。
我們的視圖處理自己的用戶接口事件,并會把相關事件映射到視圖模型。對象和它屬性與視圖模型是同步的,且通過雙向數據綁定進行更新。
觸發器(數據觸發器)允許我們進一步在視圖狀態變化后改變我們的對象屬性。
### 小結:視圖模型和模型
雖然可能會出現在MVVM中視圖模型完全對模型負責的情況,這些關系確實有一些值得關注的微妙之處.處于數據綁定的目的,視圖模型可以暴露出來一個模型或者模型屬性,而且也能夠包含獲取和操作視圖中暴露出來的屬性。
#### 優點和缺點
現在,我們完全對MVVM是什么,以及它是如何工作的,有了一個更好的了解.現在就讓我們來看看使用這種模式的優點和缺點吧:
優點:
* MVVM更加便于UI和驅動UI的構造塊,這兩部分的并行開發
* 抽象視圖使得背后所需要的業務邏輯(或者粘合劑)的代碼數量得以減少
* 視圖模型比事件驅動代碼更加容易進行單元測試
* 視圖模型(比視圖更加像是模型)能夠在不用擔心UI自動化和交互的前提下被測試
缺點:
* 對于更簡單的UI而言,MVVM可能矯枉過正了
* 雖然數據綁定可以是聲明性質的并且工作得很好,但在我們簡單設置斷點的地方,它們比當務之急的代碼更加難于調試
* 在非凡的應用程序中的數據綁定能夠創造許多的賬簿.我們也并不希望以綁定比被綁定目標對象更加重量級,這樣的境地告終
* 在大型的應用程序中,將視圖模型的設計提升到獲取足夠所需數量的泛化,會變得更加的困難
## MVVM 的低耦合數據綁定
常見到有著MVC或者MVP開發經驗的JavaScript程序員評論MVVM的時候在抱怨它會分散他們的關注點。也就是說,他們習慣在一個視圖中有相當數量的數據被耦合在了HTML標簽中。
我必須承認當我第一次體驗實現了MVVM的JavaScript框架后(例如 KnockoutJS, Knockback),我很驚訝很多程序員都想要回到一個難以維護的混淆了邏輯(JavaScript代碼)和HTML標簽做法的過去。然而現實是使用MVVM會有很多好處(我們之前說過),包括設計師能更容易的通過他們的標記去綁定相關邏輯。
在我們中間的傳統程序員,你會很開心知道現在我們能夠通過數據綁定這個特性大量減少程序代碼的耦合程度,且KnockoutJS從1.3這個版本就開始提供自定義綁定功能。
KnockoutJS 默認有一個數據綁定提供者,這個提供者搜索所有的附屬有數據綁定屬性的元素,如下面的例子:
~~~
<input id="new-todo" type="text" data-bind="value: current, valueUpdate: 'afterkeydown', enterKey: add" placeholder="What needs to be done?"/>
~~~
當這個提供者定位一個到包含有該屬性的元素時,這個工具將會分析該元素,使用當前的數據上下文來將其轉化成一個綁定對象。這種方式是 KnockoutJS百分百可以工作的方式,通過這種方式,我們可以采用聲明式的方法對元素增加綁定,KnockoutJS之后會在該層上將數據綁定到元素上。
當我們開始構建復雜的視圖的時候,我們最終就可能得到大量的元素和屬性在標記中綁定數據,這種方式將會變得很難管理。通過自定義的綁定提供者,這就不算個問題。
一個綁定提供者主要關心兩件事:
* 給定一個DOM節點,這個節點是否包含任何數據綁定?
* 如果節點回答是YES,那么這個綁定的對象在當前數據上下文中,看起來是什么樣的?
綁定提供者實現了兩個功能:
* nodeHasBindings:這個有一個DOM的節點參數,這個參數不一定是一個元素
* getBindings:返回一個對象代表當前數據上下文下的要使用的綁定
一個框架綁定提供者看起來如下:
~~~
var ourBindingProvider = {
nodeHasBindings: function( node ) {
// returns true/false
},
getBindings: function( node, bindingContext ) {
// returns a binding object
}
};
~~~
在我們充實這個提供者之前,讓我們先簡要的討論一下數據綁定屬性中的邏輯。
當使用Knockout的MVVM,我們會對將應用邏輯過度綁定到視圖上的這種方法不滿。我們可以實現像CSS類一樣的東西,將綁定根據名字賦值給元素。Ryan Niemeyer(knockmeout.net上的)之前提出使用數據類用于這個目的,來避免將展示類和數據類混淆,讓我們改造我們的nodeHasBindings 函數,來支持這個概念:
~~~
// does an element have any bindings?
function nodeHasBindings( node ) {
return node.getAttribute ? node.getAttribute("data-class") : false;
};
~~~
接下來,我們需要一個敏感的getBindings()函數。既然我們堅持使用CSS類的概念,為什么不考慮一下支持空格分割類呢,這樣可以使我們在不同元素之間共享綁定標準。
讓我們首先看一下我們的綁定長什么樣子。我們建立一個對象用于持有它們,在這些綁定處,我們的屬性名需要和我們數據類中使用的關鍵字相匹配。
> 注意:對于將使用傳統數據綁定方式的KnockoutJS應用轉化成一個使用自定義綁定提供者的不引人矚目的綁定方式。我們簡單的拉取我們所有的數據綁定屬性,使用數據類屬性來替換它們,并且像之前做的一樣,將我們的綁定放到綁定對象中去。
~~~
var viewModel = new ViewModel( todos || [] ),
bindings = {
newTodo: {
value: viewModel.current,
valueUpdate: "afterkeydown",
enterKey: viewModel.add
},
taskTooltip : {
visible: viewModel.showTooltip
},
checkAllContainer : {
visible: viewModel.todos().length
},
checkAll: {
checked: viewModel.allCompleted
},
todos: {
foreach: viewModel.todos
},
todoListItem: function() {
return {
css: {
editing: this.editing
}
};
},
todoListItemWrapper: function() {
return {
css: {
done: this.done
}
};
},
todoCheckBox: function() {
return {
checked: this.done
};
},
todoContent: function() {
return {
text: this.content,
event: {
dblclick: this.edit
}
};
},
todoDestroy: function() {
return {
click: viewModel.remove
};
},
todoEdit: function() {
return {
value: this.content,
valueUpdate: "afterkeydown",
enterKey: this.stopEditing,
event: {
blur: this.stopEditing
}
};
},
todoCount: {
visible: viewModel.remainingCount
},
remainingCount: {
text: viewModel.remainingCount
},
remainingCountWord: function() {
return {
text: viewModel.getLabel(viewModel.remainingCount)
};
},
todoClear: {
visible: viewModel.completedCount
},
todoClearAll: {
click: viewModel.removeCompleted
},
completedCount: {
text: viewModel.completedCount
},
completedCountWord: function() {
return {
text: viewModel.getLabel(viewModel.completedCount)
};
},
todoInstructions: {
visible: viewModel.todos().length
}
};
....
~~~
上面代碼中,我們丟掉了兩行,我們仍然需要getBindings函數,這個函數遍歷數據類屬性中每一個關鍵字,并從中構建最終對象。如果我們檢測到綁定對象是個函數,我們使用當前的數據調用它。我們的完成版自定義綁定提供中,如下:
~~~
// We can now create a bindingProvider that uses
// something different than data-bind attributes
ko.customBindingProvider = function( bindingObject ) {
this.bindingObject = bindingObject;
// determine if an element has any bindings
this.nodeHasBindings = function( node ) {
return node.getAttribute ? node.getAttribute( "data-class" ) : false;
};
};
// return the bindings given a node and the bindingContext
this.getBindings = function( node, bindingContext ) {
var result = {},
classes = node.getAttribute( "data-class" );
if ( classes ) {
classes = classes.split( "" );
//evaluate each class, build a single object to return
for ( var i = 0, j = classes.length; i < j; i++ ) {
var bindingAccessor = this.bindingObject[classes[i]];
if ( bindingAccessor ) {
var binding = typeof bindingAccessor === "function" ? bindingAccessor.call(bindingContext.$data) : bindingAccessor;
ko.utils.extend(result, binding);
}
}
}
return result;
};
};
~~~
我們綁定對象最后的幾行,定義如下:
~~~
// set ko's current bindingProvider equal to our new binding provider
ko.bindingProvider.instance = new ko.customBindingProvider( bindings );
// bind a new instance of our ViewModel to the page
ko.applyBindings( viewModel );
})();
~~~
我們在這里所做的是為我們的綁定處理器有效的定義構造器,綁定處理器接受一個我們用來查找綁定的對象(綁定)。然后我們可以使用數據類為我們應用程序視圖的重寫標記,像下面這樣做:
~~~
<div id="create-todo">
<input id="new-todo" data-class="newTodo" placeholder="What needs to be done?" />
<span class="ui-tooltip-top" data-class="taskTooltip" style="display: none;">Press Enter to save this task</span>
</div>
<div id="todos">
<div data-class="checkAllContainer" >
<input id="check-all" class="check" type="checkbox" data-class="checkAll" />
<label for="check-all">Mark all as complete</label>
</div>
<ul id="todo-list" data-class="todos" >
<li data-class="todoListItem" >
<div class="todo" data-class="todoListItemWrapper" >
<div class="display">
<input class="check" type="checkbox" data-class="todoCheckBox" />
<div class="todo-content" data-class="todoContent" style="cursor: pointer;"></div>
<span class="todo-destroy" data-class="todoDestroy"></span>
</div>
<div class="edit'>
<input class="todo-input" data-class="todoEdit'/>
</div>
</div>
</li>
</ul>
</div>
~~~
Nei Kerkin 已經使用上面的方式組合成了一個完整的TodoMVC示例,它可以 從 這里獲取到。 雖然上面的解釋看起來像是有許多的工作要做,現在我們就有一個一般的getBindingmethod方法要寫。比起為了編寫我們的KnockoutJS應用程序而嚴格使用數據綁定,簡單的重用和使用數據類更加的瑣碎。最終的結果是希望得到一個干凈的標記,其中我們的數據綁定會從視圖切換到一個綁定對象。
## MVC VS MVP VS MVVM
MVP和MVVM都是MVC的衍生物。它和它的衍生物之間關鍵的不同之處在于每一層對于其它層的依賴,以及它們相互之間是如何緊密結合在一起的。
在MVC中,視圖位于我們架構的頂部,其背后是控制器。模型在控制器后面,而因此我們的視圖了解得到我們的控制器,而控制器了解得到模型。這里,我們的視圖有對模型的直接訪問。然而將整個模型完全暴露給視圖可能會有安全和性能損失,這取決于我們應用程序的復雜性。MVVM則嘗試去避免這些問題。
在MVP中,控制器的角色被代理器所取代,代理器和視圖處于同樣的地位,視圖和模型的事件都被它偵聽著并且接受它的調解。不同于MVVM,沒有一個將視圖綁定到視圖模型的機制,因此我們轉而依賴于每一個視圖都實現一個允許代理器同視圖去交互的接口。
MVVM進一步允許我們創建一個模型的特定視圖子集,包含了狀態和邏輯信息,避免了將模型完全暴露給視圖的必要。不同于MVP的代理器,視圖模型并不需要去引用一個視圖。視圖可以綁定到視圖模型的屬性上面,視圖模型則去將包含在模型中的數據暴露給視圖。像我們所提到過的,對視圖的抽象意味著其背后的代碼需要較少的邏輯。
對此的副作用之一就是視圖模型和視圖層之間新增的的用于翻譯解釋的一層會有性能損失。這種解釋層的復雜度根據情況也會有所差異——它可能像復制數據一樣簡單,也可能會像我們希望用視圖理解的一種形式去操作它們,那樣復雜。由于整個模型是現成可用的,從而這種操作可以被避免掉,所以MVC沒有這種問題。
## Backbone.js Vs KnockoutJS
了解MVC,MVP和MVVM之間的細微差別是很重要的,然而基于我們已經了解到的東西,開發者最終會問到是否它們應該考慮使用KnockoutJS而不是Backbone這個問題。下面的一些相關事項對此可能有些幫助:
* 兩個庫都設計用于不同的目標,它常常不僅僅簡單的知識選擇MVC或者MVVM的問題。
* 如果數據綁定和雙向通信是你主要關注的問題,KnockoutJS絕對是應該選擇的方式。實踐中任何存儲在DOM節點中的值或者屬性都能夠使用此方法映射到Javascript對象上面。
* Backbone在同RESTful服務的易于整合方面有其過人之處,而KnockoutJS模型就是簡單的Javascript對象,而更新模型所需要的代碼也必須要由開發者自己來寫。
* KnockoutJS專注于自動的UI綁定,如果嘗試使用Backbone來做的話則會要求更加細節的自定義代碼。由于Backbone自身就意在獨立于UI而存在,所以這并不是它的問題。然而Knockback也確實能協助并解決此問題。 使用KnockoutJS,我們能夠將我們自己擁有的函數綁定到視圖模型觀察者上面,任何時候觀察者一旦發生了變化,它都會執行。這允許我們能夠擁有同在Backbone中發現的一樣級別的靈活性。
* Backbone內置有一個堅實的路由解決方案,而KnockoutJS則沒有提供路由供我們選擇。然而人們如果需要的話,可以很容易的加入這個行為,使用Ben Alman的BBQ插件或者一個像Miller Medeiros優秀的Crossroads就行了。
總結下來,我個人發覺KnockoutJS更適合于小型的應用,而Backbone的特性在任何東西都是無序的場景下面才會是亮點。那就是說,許多開發者兩個框架都已經使用過來編寫不同復雜度的應用程序,而我建議在一個小范圍內兩種都嘗試一下,在你決定哪一種能更好的為你工作之前。
- 前言
- 簡介
- 什么是設計模式?
- 設計模式的結構
- 編寫設計模式
- 反模式
- 設計模式的分類
- 設計模式分類概覽表
- JavaScript 設計模式
- 構造器模式
- 模塊化模式
- 暴露模塊模式
- 單例模式
- 觀察者模式
- 中介者模式
- 原型模式
- 命令模式
- 外觀模式
- 工廠模式
- Mixin 模式
- 裝飾模式
- 亨元(Flyweight)模式
- JavaScript MV* 模式
- MVC 模式
- MVP 模式
- MVVM 模式
- 最新的模塊化 JavaScript 設計模式
- AMD
- CommonJS
- ES Harmony
- JQuery 中的設計模式
- 組合模式
- 適配器模式
- 外觀模式
- 觀察者模式
- 迭代器模式
- 惰性初始模式
- 代理模式
- 建造者模式
- jQuery 插件的設計模式
- JavaScript 命名空間模式
- 總結
- 參考