輕量級前端MVVM框架avalon - 控制器


引子:

最近工作挺忙,avalon只能斷斷續續的寫下去了,大概看了下angular的源碼,看到小一半就比較難堅持了,是塊硬骨頭,慢慢啃吧

不過angular的的文檔中用詞還是很優雅:

  • HTML編譯器
  • 指令
  • 編譯
  • 鏈接
  • 過濾器
  • 注入器
  • 控制器
  • 管道

      等等…看起來覺得老高級,其實avalon也間接的部分實現,原理也不是很復雜

avalon版本更新很快,新版的加入了AMD規范的模塊加載器,還修復了很多BUG,不過相信短期內實現的核心還是不會變化,所以我依然以現在的版本分析為主


編譯期

  • 視圖背后的代碼就是控制器,在mvvm中就是vm視圖模型,它的主要工作就是構造模型,並把模型與回調方法一並發送到視圖,視圖可以看作作用到模版HTML上的投影
  • 所以在編譯階段,我們的控制器就會把用戶定義的數據模型給構造出來
  • avalon中的modelFactory工廠方法構造出的model對象其實就是真正的控制器了,至於構造出來的控制器如何注入到視圖上的,等以后分析到HTML編譯器雙向綁定吧
  • model 本來是系統內部定義的一個臨時對象,將控制器和avalon的作用域對象給關聯起來

接上一節

收集用戶定義的scope在過濾的時候做了2個處理

 callGetters.push(accessor);
 callSetters.push(name);

收集控屬性賦監與計算屬性,是為了在初始化scpoe中的代碼未處理的方法

 


處理監控屬性

 

    //給控屬性賦監值,調用對應監控屬性的set ->accessor方法
    callSetters.forEach(function(prop) {
       // model.firstName = '司徒' ->調用了 model.firstName->set->accessor方法
        model[prop] = scope[prop]; //為空對象賦值
    });

遍歷監控屬性收集器,給初始化的空model對應的方法賦值

這里注意各重點,賦值的的時候實際是調用的accessor方法,因為set get給轉換過了

accessor 源碼

 

其實源碼注釋很清楚了,我們歸納下執行的流程

  • 判斷參數是調用set還是get方法
  • stopRepeatAssign //阻止重復賦值,是這factory.apply(0, deps);重置上下文的時候處理的
  • 監控數組處理
  • 更新json  //收集原始的定義
  • notifySubscribers  //更新依賴,就是當前的操作會觸發與之相關
  • model.$events 觸發訂閱的自定義事件

notifySubscribers  其實就是關鍵的執行點,執行當前作用域所依賴的所有的,這個在雙向綁定的時候就可以仔細討論了

 


 處理計算屬性:

 

image

監控屬性涉及用戶定義的處理,所以要做很多關聯的處理

流程:

  • 收集依賴關系
  • 處理用於定義的get方法
  • 更新json
  • 返回定義函數的結果

Publish 對象是將函數曝光到此對象上,方便訪問器收集依賴

fn.nick 就是對應的計算屬性方法名稱,在過濾的時候 accessor.nick = name;附上的

同樣執行了accessor方法,由於沒有傳遞參數,實際上就是在處理收集依賴關系了

 

accessor 源碼

 

 

collectSubscribers方法

image

很明顯的處理,取出開始push到的Publish的處理回調,取出依賴列表,合並

ensure 法只有當前數組不存在此元素時只添加它

image

所有此時的 subscibers關聯就有值了

最后執行定義的get方法,更新json

image

注意的一點

image

這里又涉及到取值的問題,所以又會關對應的執行各自的accessor

所以這里會進行一次收集依賴了

 


在轉換的完畢model后,會給model增加訂閱的特性與一些屬性

  • model.$json = json;  //純凈的js對象,所有訪問器與viewModel特有的方法屬性都去掉

增加事件訂閱

  • model.$events = {}; //VB對象的方法里的this並不指向自身,需要使用bind處理一下
  • model.$watch = Observable.$watch.bind(model);//用於監聽ViewModel中的某屬性變化,它將新值與舊值都傳給回調
  • model.$unwatch = Observable.$unwatch.bind(model);//卸載$watch綁定的回調
  • model.$fire = Observable.$fire.bind(model); //觸發$watch指定的回調

ViewModel的ID,方便通過avalon.models[$id]訪問

  • model.$id = generateID();

判斷是否為模型中的原始數據

  • model.hasOwnProperty 方法

 

最后返回工廠轉化后的model對象

 


主方法入口

avalon.define

image

其實這里有一種重點

image

作者再次把定義的模型給執行了一遍,用意呢?

請看

vm.xxx = 1;
 
vm.fullName = fucntion(){
       vm.xxxx               
 
}
 

在定義的VM中的方法中,如果再次訪問vm.xxx屬性,

這時候內部引用不對了 VM還是指向原來的普通JS對象,而不是真正的VM所以需要apply一次,改變

那么有個精妙的思路:

我們 factory.apply(0, deps); //重置它的上下文

所以把方法執行一次把內部引用換給model
因為轉換了模型關系,所以監控屬性與計算屬性都會有對應的set get操作了,相對應的上下文也變成了vm了
stopRepeatAssign return 阻止了,防止重復賦值

 

avalon.models[name] = model; 掛到了全局的models中,方面以后使用

 

下章就開始講HTML編譯器與指令,如何真正開始工作了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM