本文的靈感是來自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已經在外國非常盛行,希望國人不要再落后太多了。