一個項目是由許多人分工寫的,所以必需要合理地拆散,因而有了模塊化。體如今工做上,PM一般它這爲某某版塊,某某頻道,某某頁面。某一個模塊,必須是包含其固有的數據,樣式,HTML與處理邏輯。在jQuery時代,奉行的是「無侵入式javascript」,頁面雖然是拆成一塊塊,但最後是經過PHP等後端模板合併起來,而且把第一屏的數據直接灌進去,接着是無盡的選擇某些元素進行處理,選擇某些元素進行處理。javascript裏面是滿屏的CSS表達式,若是不一一對着HTML頁面,這是沒法閱讀的。換言之,jQuery很容易產生readyOnly的代碼。javascript
avalon是引入分層構架,視圖就是視圖,數據就是數據,JS裏面是操做數據,不會再操做視圖,涇渭分明。視圖,換言之就是最初作好的那些HTML片斷,只須要在裏面添加上ms-controller指令(或叫綁定屬性),指定其將要做用的ViewModel的ID,而後在它裏面添加其餘綁定就好了。數據,特指是ViewModel,avalon是經過define方法定義,目的是實現「操做數據即操做DOM」,今後咱們再也用不上什麼操做DOM的API,javascript代碼量當即減小了一半以上,條理更清晰,更易維護。html
ViewModel的定義是一個重頭戲。在入門教程裏,是這樣定義的:java
var model = avalon.define("test", function(vm) { vm.firstName = "司徒" vm.lastName = "正美" vm.fullName = {//一個包含set或get的對象會被當成PropertyDescriptor, set: function(val) {//裏面必須用this指向scope,不能使用scope var array = (val || "").split(" "); this.firstName = array[0] || ""; this.lastName = array[1] || ""; }, get: function() { return this.firstName + " " + this.lastName; } } vm.arr = ["aaa", 'bbb', "ccc", "ddd"] vm.selected = ["bbb", "ccc"] vm.checkAllbool = vm.arr.length === vm.selected.length vm.checkAll = function() { if (this.checked) { vm.selected = vm.arr } else { vm.selected.clear() } } }) model.selected.$watch("length", function(n) { model.checkAllbool = n === model.arr.size() })
有兩個參數,第一個定義ID,第二個是定義ViewModel自己的數據,它有什麼監聽屬性啊,計算屬性啊,一些特殊的指令啊,$watch回調啊用戶須要區分vm與model的區別,有什麼須要注意的地方(這些在入門教程都有介紹)。在1.3.3中,添加了如今這種新的定義方式,只要傳入一個對象:git
var model = avalon.define({ $id: "test", firstName: "司徒", lastName: "正美", fullName: {//一個包含set或get的對象會被當成PropertyDescriptor, set: function(val) {//裏面必須用this指向scope,不能使用scope var array = (val || "").split(" "); this.firstName = array[0] || ""; this.lastName = array[1] || ""; }, get: function() { return this.firstName + " " + this.lastName; } }, arr: ["aaa", 'bbb', "ccc", "ddd"], selected: ["bbb", "ccc"], checkAllbool: false, checkAll: function() { if (this.checked) { model.selected = model.arr } else { model.selected.clear() } } }) model.checkAllbool = model.arr.length === model.selected.length model.selected.$watch("length", function(n) { model.checkAllbool = n === model.arr.size() })
當咱們將一個對象傳進avalon.define方法,它將返回一個全新的對象,它添加了許多$方法與屬性,而且原來的屬性都變得很是奇怪,在控制檯下能夠看到它們都對着一個set方法一個get方法。ecma262 v5稱之爲訪問器屬性(named accessor properties)。固然不一樣的人有不一樣的譯法,你們想詳細瞭解此屬性的特性,能夠閱讀如下連接,這是avalon能讓你修改屬性就能同步視圖的關鍵!github
注意,咱們全部定義的VM都存放在avalon.vmodels對象上。打開咱們上一節寫的項目,在firebug下輸入avalon.vmodels能夠查看到:
web
那麼爲了應用這些ViewModel,咱們就須要用到ms-controller、 ms-important、ms-skip這三個指令。ms-controller在頁面上表現爲一個特殊的屬性,其屬性值爲ViewModel的$id,表示將在此元素或其子孫元素上圈定它的做用域範圍,但若是這些HTML存在它沒有的屬性,它能夠向上查找上一級的ViewModel的屬性。換言之,ms-controller能夠互相套嵌的。 ms-important的用法與ms-controller差很少,但它不會向上查找。ms-skip註明這塊區域不該用任何的ViewModel的屬性,它裏面的任何指令(綁定屬性)都會失效。由於{{}}也算一種指令,而任何指令在被掃描後都會被移除,若是咱們想保留某個區域的{{}},就須要用到ms-skip。有關ms-controller, ms-important的詳細用法可見這裏。後端
上面的ViewModel再配合一些HTML代碼,就是實現一些用jQuery很是費勁才能實現的功能api
<div ms-controller="test"> <p>First name: <input ms-duplex="firstName" /></p> <p>Last name: <input ms-duplex="lastName" /></p> <p>Hello, <input ms-duplex="fullName"></p> <div>{{firstName +" | "+ lastName }}</div> <ul> <li><input type="checkbox" ms-click="checkAll" ms-checked="checkAllbool"/>全選</li> <li ms-repeat="arr" ><input type="checkbox" ms-value="el" ms-duplex="selected"/>{{el}}</li> </ul> </div>
你們能夠在這裏看到實際運行效果。數組
再細說一下ViewModel(咱們一般也簡稱爲VM)的一些屬性。ruby
$id: VM的ID,方便在avalon.vmodels裏查找到它,或用在ms-controller、ms-important上。
$events:裏面存放着各類回調,它們是經過$watch方法添加的。
$watch:這是一個方法,有兩個參數,第一個是VM中的某一個屬性名,只能這個VM的直接子屬性名,第二個是回調函數,當此屬性發生改變時,就會執行此回調。回調裏會依次傳入它的新老屬性值。
$unwatch:移除某個屬性的回調。
$fire:手動觸發此回調。
$accessors:放置與監聽屬性相連動的視圖刷新函數,當咱們改變某一屬性時,框架就會在這裏找到對應的視圖刷新函數,傳入當前值,實現對視圖的同步。
$123323213:它的格式是$加上一串數字,它是用於放置監控數組的視圖刷新函數,當咱們調用監控數組的方法時,框架就此根據當前數組的個數與排列順序,從新渲染對應的區域。它與$accessors同樣,不開放給用戶調用的。
$model:就是ViewModel的淨化版,沒有$XXX屬性,訪問器屬性所有還原爲普通屬性,專門用於提交到後臺用。固然咱們提交後臺,還須要用JSON.parse(JSON.stringify(VM.$model))處理一下,將裏面的函數幹掉。
如今說的仍是基本用法,$watch、$unwatch、 $fire其實遠遠比你想象的強大,你們感興趣的話,能夠到這裏瞭解其高級用法。
好了,咱們把上面的代碼放進上一節,修改aaa.js, aaa.html,感覺一下一個複雜的ViewModel的應用吧。
有了ViewModel後,咱們的代碼就顯得很是有內聚力,本身知道要做用於視圖的哪一塊區域,而且不用本身操心如此修改DOM,變成單純的數據操做。
而數據操做是須要在頁面定義一些指令(咱們稱之爲綁定屬性與插值表達式)。如今最簡單的有兩個,{{ prop }}是直接將屬性輸出到頁面,若是它存在尖括號,會原樣輸出,不會轉換爲HTML標籤。{{ prop | html}}則相反,好比這個屬性的值爲」xxxxerer」,那麼裏面就真會轉爲一個b標籤。其實{{prop}},{{prop|html}}還有另外一種寫法, ms-text=」prop」, ms-html=」prop」。有關這些綁定的屬性詳細用法,咱們下一節講述。
本章節的代碼能夠從這裏下載。