(轉載)前端MVVM框架avalon揭祕 - 雙向綁定原理

avalon你們可能不熟悉,可是Knockout估計或多或少聽過用過,那麼說說KO的幾個概念css

  1. 監控屬性(Observables)和依賴跟蹤(Dependency tracking)
  2. 聲明式綁定(Declarative bindings)
  3. 模板(Templating)

本章主要提到 監控屬性 與 依賴跟蹤(後更名叫計算屬性)git

監控顧名思義,監聽着你設定目標的變化,換句話說可以通知訂閱者它的改變以及自動探測到相關的依賴。github

計算屬性,就是依賴監控屬性變化而自動調用處理更新數組

KO的一個例子ruby

若是你已經有了監控屬性firstName和lastName,你想顯示全稱怎麼辦? 這就須要用到依賴監控屬性了 – 這些函數是一個或多個監控屬性, 若是他們的依賴對象改變,他們會自動跟着改變。mvvm

例如,下面的view model,函數

var viewModel = {
    firstName: ko.observable('Bob'),
    lastName: ko.observable('Smith')
};

… 你能夠添加一個依賴監控屬性來返回姓名全稱:this

viewModel.fullName = ko.dependentObservable(function () {
    return this.firstName() + " " + this.lastName();
}, viewModel);

而且綁定到UI的元素上,例如:編碼

The name is <span data-bind="text: fullName"></span>

… 無論firstName仍是lastName改變,全稱fullName都會自動更新(無論誰改變,執行函數都會調用一次,無論改變成什麼,他的值都會更新到UI或者其餘依賴監控屬性上)spa

 

OK

KO是怎麼實現雙向機制的呢?

  • 經過轉換VM中全部要監聽的東西爲函數,而後執行它們,獲得某一時刻中,一共有多少函數被執行,將它們放到棧中,最底的就是最早被執行的,它上面的就是此函數所依賴的函數,從而獲得依賴關係。
  • 而後設計一個觀察者模式,從上面的依賴檢測中,將依賴函數做爲被依賴者(最早執行的那個的)的訂閱者,之後咱們對被依賴者進行賦值時,就會通先訂閱者更新自身,從而造成一個雙向綁定鏈。
  • knockout會將視圖中的綁定屬性進行轉換,分解出求值函數與視圖刷新函數,視圖刷新函數依賴於求值函數,而求值函數亦依賴於咱們VM中的某些屬性(這時,它們都轉換爲函數),在第一次掃描時,它們會加入對應屬性的訂閱者列隊中, 從而VM中的某個屬性改變,就會自動刷新視圖、

 

豬腳登場

avalon實現雙向綁定跟ko的實現其實大同小異,可是ko的實現異常的複雜,avalon則清晰不少

上列子,而後分析

HTML結構

複製代碼
<div id='box' ms-controller="box">
    <div style=" background: #a9ea00;" ms-css-width="w" ms-css-height="h"  ms-click="click"></div>
    <p>{{ w }} x {{ h }}</p>
    <p>W: <input type="text" ms-model="w" data-event="change"/></p>
    <p>H: <input type="text" ms-model="h" /></p>
</div>
複製代碼

JS

複製代碼
avalon.define("box", function(vm) {
        vm.w = 100;
        vm.h = 100;
        vm.click = function() {
            vm.w = parseFloat(vm.w) + 10;
            vm.h = parseFloat(vm.h) + 10;
        }
    })
    avalon.scan(document.getElementById('box')
複製代碼

就是官網提供的一個DEMO  http://rubylouvre.github.io/mvvm/

分析HTML結構:

  1. ms-controller="box"  做用域範圍
  2. ms-css-width="w"     css樣式綁定寬度
  3. ms-css-height="h"    css樣式綁定高度
  4. ms-click="click"         綁定click處理事件
  5. {{ w }} x {{ h }}     插值表達式 數據填充
  6. ms-model="w"         更名叫ms-duplex 雙向綁定

 

JS處理中:

  1. avalon.define   構建一個view model視圖模型  box就是做用域範圍
  2. vm.w = 100;   定義一個監控屬性寬,默認值是100
  3. vm.h = 100;   定義一個監控屬性高,默認值是100
  4. vm.click          定義一個click處理方法
  5. avalon.scan     掃描節點指定綁定

 

view model視圖模型的構建在之前已經講過了,這裏主要講下雙向綁定的構建及處理的原理

1.構建VM的時候,對監控屬性進程轉化處理,生成一個監控對象

監控對象是經過Object.defineProperty轉換過的處理函數,因此在setter,getter時候會調用轉化的處理函數,這個用戶是不可見的

賦值時處理 setter

複製代碼
var old = value;
if (valueType === "array" || valueType === "object") {   //監控數組
    if (value && value.$id) {
        updateViewModel(value, neo, Array.isArray(neo))
    } else if (Array.isArray(neo)) {
        value = Collection(neo)
        value._add(neo)
    } else {
        value = modelFactory(neo, neo)
    }
} else {
    value = neo
}
accessor.value = value;
model[name] = value && value.$id ? value.$model : value;
//值變化了,通知頂層改變
notifySubscribers(accessor);
vmodel.$fire && vmodel.$fire(name, value, old)
複製代碼

取值時處理 getter

collectSubscribers(accessor); //收集視圖函數
     return value

操做時

vm.w = 100  setter,就會默認調用賦值處理函數

vm.w  一樣,getter 調用取值函數

這樣方式,比ko的this.firstName() + " " + this.lastName();  友愛多了,由於KO轉換的是處理函數,必需要函數調用。。。別提多變扭

 

關鍵點:

collectSubscribers  //收集視圖函數

notifySubscribers //值變化了,通知頂層改變

這個2個方法,用來處理依賴關係的

 

實現的流程:

預處理過程:

  1. 生成監控屬性,其中監控屬性subscribers用來收集依賴處理的回調
  2. 掃描DOM節點上的對應的屬性編碼 好比,ms-css-width="w"
  3. 根據css類型找到對應bindingHandlers處理句柄函數
  4. 經過parseExpr分解出求值函數
  5. 經過watchView函數生成視圖更新函數
  6. 視圖更新函數updateView加入到當前對應監控屬性的subscribers隊列中,造成依賴關係

交互時:

  1. 用戶點擊某個經過ms-click="click」 綁定事件的元素(當value變化時改變model的值)視圖通知模型
  2. 執行預先生成updateModel函數,經過執行’取值時處理’通知notifySubscribers(accessor)
  3. 執行accessor中subscribers的全部依賴函數,從而更新全部依賴

 畫了張圖。。

本文只是很簡單的說了下監控屬性大概的邏輯,還有計算屬性的處理,轉化,調用,以後會分解

相關文章
相關標籤/搜索