在MVC里,View是可以直接訪問Model的!從而,View里會包含Model信息,不可避免的還要包括一些業務邏輯。 MVC模型關注的是Model的不變,所以,在MVC模型里,Model不依賴於View,但是 View是依賴於Model的。不僅如此,因為有一些業務邏輯在View里實現了,導致要更改View也是比較困難的,至少那些業務邏輯是無法重用的。
MVVM在概念上是真正將頁面與數據邏輯分離的模式,它把數據綁定工作放到一個JS里去實現,而這個JS文件的主要功能是完成數據的綁定,即把model綁定到UI的元素上。
有人做過測試:使用Angular(MVVM)代替Backbone(MVC)來開發,代碼可以減少一半。
此外,MVVM另一個重要特性,雙向綁定。它更方便你同時維護頁面上都依賴於某個字段的N個區域,而不用手動更新它們。
前端MVVM框架設計及實現(一)
最近抽出點時間想弄個dom模塊化的模板引擎,不過現在這種都是MVVM自帶的,索性就想自己造輪子寫一個簡單的MVVM框架了
借鑒的自然還是從正美的avalon開始了,我記得還是去年6月寫過一個系列的avalon源碼分析的,不過那時候0.7版本,不夠健全,現在已經好太多了
框架是面向一個領域,提供一套解決方案,那么我們用前端的MVVM能為我們帶來什么便利?
- 關注點分離
- 操作數據即操作DOM
- 動態模板
關注點分離是MVVM與身俱來的,操作數據即操作DOM,是VM中的訪問器帶來的,動態模板是流程綁定實現的。
關於MV*的討論太多了,這里不在討論,我們重點就是分析如何實現前端MVMM框架
Avalon 地址 https://github.com/RubyLouvre/avalon
學會MVVM需要先會哪些東西?
1. javascript語言基礎(作用域,原型鏈,閉包等等)
2. 簡單設計模式,基本的數據結構
3. 閱讀或者寫過jQuery源碼
為什么要這樣說呢,因為avalon就是這些東東的綜合體!
我是以avalon為藍本,按照作者是思路模仿實現的,當然avalon的代碼有4000多行,新手如果去學習的話估計無從下手,也力不從心
為什么呢?簡單的說實現的手段有點另類,寫的代碼有點狂野(請原諒我不知道如何形容),不過用戶體驗倒是不錯!
簡單的看下代碼結構
<div ms-controller="box"> <div style=" background: #a9ea00;" ms-css-width="w" ms-click="click"></div> <p>{{ w }}p> </div> <script> avalon.define("box", function(vm) { vm.w = 100; vm.click = function() { vm.w = parseFloat(vm.w) + 10; } }) </script>
針對這個代碼結構,我們要明白:
1:為什么要自定義大量標記(聲明式綁定)
這就是MVVM 最原始的意義,數據邏輯展現分離。表現就是 數據 js邏輯代碼 htmlcss展現
所以再HTML里加結構是自然而然的事情,如果html都用js生成,那就跟mvvm搭不上邊
2:avalon.define里面為什么不需要操作dom?
在MVVM中,數據是核心,由於VM與V之間的雙向綁定,操作了VM中的數據(當然只能是監控屬性),就會同步到DOM,我們透過DOM事件監控用戶對DOM的改動,也會同步到VM。
本章我們就實現第一步:搭建基本的分層結構,實現雙向通知機制
第一版實現:300行代碼,請對照分析看源碼 https://github.com/JsAaron/aaMVVM
針對上面2個問題,我們看看如何才能做到操作數據即操作dom呢?
簡單的說一下實現是思路:大家可以down下git的aaMVVM對照下,比原版的4000行代碼友愛多了!
我們知道在MVVM中,M只是一個過客,它與其他表示業務狀態的東西融入VM(ViewModel)中。ViewModel是一個狀態的集合,當然還拖家帶口監控着大量的回調
所以ViewModel就承載的幾乎所有的功能,在avalon中ViewModel就包含所有的數據與方法的定義,溝通着V與M,起到承上啟下的作用~
視圖模型如何與數據跟視圖關聯起來?
通過avalon.define定義的vm中的屬性與方法都與對應的html結構中的標記有映射關系,所以改變vm中的數據與之關聯的dom就會自動刷新
分析下
vm.w = 100
當模型的數據改變為100時,對應的視圖中div的寬度為100, 文本<p>100</p> ,可見修改一個數據在同一個控制器內與之關聯的2個映射動作都將會修改
一個是css操作,一個是text賦值
從這個操作我們可以大膽推測下vm.v中應該有一個列表,記錄了當前控制器下對應的映射操作(多個)
為了實現set與get方法,avalon也類似emberjs,采用了Object.defineProperty
我用最簡單的代碼模擬下實現
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>測試VM</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> </head> <body> <div id='box' ao-controller="box"> <div id='aa-attr' style="background: #a9ea00;width:100px;height:100px;" ao-css-width="w" ao-click='click' ></div> <p id='aa-text'>{{ w }}</p> </div> </body> </html>
也就是說
vm.w = 100 即要修改style也要修改p,就是一對多的關聯方式
所以在avalon中針對每一個監控屬性,都會生成set與get的訪問控制器,那么在每一個監控屬性的訪問控制器里面都會有一個
accessor[subscribers] = [] //訂閱者數組,這樣的東東來存放與之相關的依賴
當我們觸發 vm.w = 100時,就會觸發w:set方法,取出accessor[subscribers]中的依賴,從而各執執行,這樣就實現了依賴執行了
對應的方法:自動更新自身的依賴
function notifySubscribers(accessor) { var list = accessor[subscribers] if (list && list.length) { var args = [].slice.call(arguments, 1) for (var i = list.length, fn; fn = list[--i];) { var el = fn.element fn.handler(fn.evaluator.apply(0, fn.args || []), el, fn) } } }
上面只是簡單的思路,真正實現的時候真要做到大而全的框架,考慮的問題可不是那么簡單的事
1 框架是怎么解釋聲明式綁定的語法
2 如何把解析后的語法生成對應的處理句柄
3 用戶的定義如何生成vm模型
4 如何收集這些依賴
5 如何自動更新依賴映射
GitHub上會同步更新最新的實現,。。。敬請關注~
Fork https://github.com/JsAaron/aaMVVM