本文的靈感是來自Halower的這篇博文,他是使用knockout與jQuery實現的。不過我覺得MVVM本來就強大的事件綁定功能,因此用jQuery 是多此一舉。另,他也用了一些面向對象的寫法。我個人認為,純數據就該好好當純數據,作為數據模型(M)而存在,想操作數據,則交由視圖模型(VM)。在angularjs流行的一些成規,都是要求大家不要自己操作DOM,DOM是框架自行幫你偷偷搞定。這也與avalon一直提倡的“操作數據即操作DOM”的理念相符。由於avalon巧妙地利用了Object.defineProperty, __defineSetter__, __defineGetter__, VBScript等方法把等於號(=)重載了,因此與視圖的同步就變得比其他MVVM更隱秘神奇。那么接着下來,讓我們看看avalon是如何實現這個功能吧。
首先是視圖層,里面的綁定屬性其實可以在VM中的屬性定了下來再添加。這也涉及MVVM另一個優勢,分離關注點,因此切圖與JS編程可以同時進行。由於JS代碼不進行DOM操作,頁面長得怎么樣也無所謂,我又不需要選擇器引擎!
<div ms-controller="grid" class="grid" >
<div>
<p> <input ms-duplex="id">
<input ms-duplex="name">
<input ms-duplex="score" data-duplex-event="change"></p>
<p><button ms-click="add"> add</button></p>
</div>
<p>共{{array.size()}}條------------------合計{{total}}分</p>
<table>
<thead>
<tr>
<th>ID</th> <th>姓名</th> <th>分數</th> <th>操作</th>
</tr>
</thead>
<tbody ms-each-el="array">
<tr>
<td>{{el.id}}</td>
<td>{{el.name}}</td>
<td>{{el.score}}</td>
<td align="center"><a ms-click="$remove" href="javascript:void(0)">移除</a></td>
</tr>
</tbody>
</table>
<textarea ms-value="JSON.stringify(array.$model)" style="width:90%;height:220px;"></textarea>
</div>
樣式隨便弄弄:
.grid table{
border:1px solid #000;
width:500px;
border-collapse: collapse;
}
.grid button{
width:400px;
background: orange;
}
.grid table th, .grid table td{
border:1px solid #000;
padding: 2px 5px;
}
從HTML結構來看,分為兩部分,一個是用於輸入數據,另一個是呈現所有數據,數據上方還有個小統計。輸入數據部分有三個輸入項,我們對分數中進行校驗,只保證其是數字就行了,目的是為了相加,因為input的value屬性總為一個字符串類型。下方有個按鈕,用於提交。呈現區為一個table,所有MVVM框架都支持數組循環輸出,我的與angular走得很近。在循環區域,里面還內置一個$remove方法,用於刪除監控數組中的某一個元素,這個比knockout人性化多了。下面是JS部分,你是看不到一句操作DOM的代碼。
//如果大家對avalon不熟悉,可以參看這篇入門教程 http://www.cnblogs.com/rubylouvre/p/3181291.html
avalon.ready(function() {
var model = avalon.define('grid', function(vm) {
vm.id = ""
vm.name = ""
vm.score = 0
vm.total = 0
vm.add = function() {
if(vm.id && vm.name ){
vm.array.push({
id: vm.id,
name: vm.name,
score: vm.score
})
}
}
vm.array = []
});
model.$watch("score", function(a) {
var a = Number(a) || 0
a = a > 100 ? 100 : a < 0 ? 0 : a//強制轉換為0~100間
model.score = a
})
model.array.$watch("length", function() {
var a = 0
model.array.forEach(function(el) {
a += el.score//求得總數
})
model.total = a;
model.id = ""
model.name = ""
model.score = 0
})
model.array = [
{id: "d1", name: "李世民", score: 67},
{id: "d2", name: "贏政", score: 90}
]
avalon.scan();
});
我們在define方法中定義了VM所有用到的數據,什么id, name, score, array, 還有需要綁到視圖中的add方法。數據校驗或數據變動時需要做的操作,我們是用$watch實現,它們被安排到define方法外,這是一個好主意。然后,就沒有然后了!這就是MVVM的神奇之處,因為我們在視圖中使用了{{}}, ms-click已經指明了它們的行為。因此當數據變動時,框架自然明白自己該什么做。
目前我與5群的一些人已經將avalon應用於公司的生產環境,反應還是不錯。雖然目前還不時冒出一些怪異的BUG,但難度不至於影響我們的進度,基本上半天就能修,比如說IE9-10的option標簽的value問題,firefox 全系列下,未插入DOM樹的元素的display樣式取值問題,這是jQuery也沒報到的新東西。有的話,我在做mass Framework時已經遇到過了。MVVM是一個全新的領域,要求對用戶全面隱藏DOM操作,並將整個DOM樹作為一個動態模板或數個復用的子模板,另外,DOM樹其實也被我框架看作為一個Ioc容器的配置文件。如此種種,遭遇新的問題在所難免,但只要方向是對的,這就是康庄大道。雖然它與jQuery走的是一條截然不同的路,但明顯優於jQuery。jQuery與DOM存在強依賴,導致維護成本奇高。這正是西方繼jQuery后,又孜孜不倦發明backbone, canjs, knockout, emberjs, angular的原因。現在國內的技術步伐普通比外國慢兩三年,現在前端MVVM已經在外國非常盛行,希望國人不要再落后太多了。
