前端MVVM框架avalon揭秘 - HTML編譯器


MVVM試圖更加清晰的講用戶界面(UI)開發從應用程序的業務邏輯與行為中心分離,因為,很多這樣的模式的實現都需要利用聲明式數據綁定來實現講View(視圖)工作從其他層分離

所以出現了一大堆自定義的聲明式的語法:

如:Avalon

  1. 作用域綁定(ms-controller, ms-important)
  2. 模板綁定(ms-include)
  3. 數據填充(ms-text, ms-html)
  4. 類名切換(ms-class, ms-hover, ms-active)
  5. 事件綁定(ms-on,……)
  6. 顯示綁定(ms-visible)
  7. 插入綁定(ms-if)
  8. 雙工綁定(ms-duplex,原來的ms-model)
  9. 樣式綁定(ms-css)
  10. 數據綁定(ms-data)
  11. 布爾屬性綁定(ms-checked, ms-selected, ms-readonly, ms-disabled, ms-enabled)
  12. 字符串屬性綁定(ms-title, ms-src, ms-href……)
  13. 萬能屬性綁定(ms-attr)
  14. 萬能綁定(ms-bind)
  15. 數組循環綁定(ms-each)
  16. 對象循環綁定(ms-with)
    等等………

顧名思義,自定義聲明語法,那么游覽器本身是不能識別的,那么如何游覽器能過識別自定義的HTML語法,它能讓你講行為關系到HTML元素或者屬性上,甚至能讓你創造具有自定義行為的新元素呢,我們暫且講這個過程稱之為“HTML編譯”吧。

我們先看一段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>
    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'));

HTML結構中充斥了大量的ms開頭的自定義標簽,還有{}插值表達式。。等等

聲明1:

ms-controller="box"

avalon提供ms-controller, ms-important來指定VM在視圖的作用范圍。比如有兩個VM,它們都有一個firstName屬性,在DIV中,如果我們用 ms-controller="VM1", 那么對於DIV里面的{{firstName}}就會解析成VM1的firstName中的值。

聲明2:

ms-css-width="w" ms-css-height="h" 

用來處理樣式

聲明3:

ms-click="click"

avalon通過ms-on-click或ms-click進行事件綁定,並在IE對事件對象進行修復,並統一了所有瀏覽器對return false的處理

 

其實就是把部分的行為操作提升到了dom上了,然后有框架在后台給你處理好,通過加入各種自定義的屬性可以讓任何的HTML元素都實現這樣的行為

 

具體看源碼的執行流程:

總的來說就是匹配每一給節點上的屬性,通過匹配分配到指定的bindingHandlers處理函數上,之后的處理本章不暫時不涉及

//掃描入口
avalon.scan = function(elem, vmodel)

//掃描子節點
function scanNodes(parent, vmodels, callback) 

//開始掃描
function scanTag(elem, vmodels)

//掃描文本
function scanText(textNode, vmodels)

//掃描表達式
function scanExpr(str)

//掃描屬性節點
function scanAttr(el, vmodels)

//抽取綁定
function executeBindings(bindings, vmodels)

//抽取文本綁定
function extractTextBindings(textNode)

 

看看命名就大概能猜出函數的作用了

1.入口函數  avalon.scan

avalon.scan

默認從文本的根documentElement開始,如果傳遞了第一個elem,那么就是指定了掃描的作用域了,類似 jQuery( selector, [ context ] )

 

2. 執行掃描 scanTag

avalon.scan
  • 依次要檢測是當前元素上是否有ms-skip,ms-important,ms-controller屬性,用於最開始的處理
  • 如果ms-controller存在就取出vm視圖模型對象
  • 清除這個自定義屬性
  • 執行sacnAttr 屬性掃描

 

3. 掃描屬性節點 scanAttr

avalon.scan

attributes 屬性返回包含被選節點屬性的 NamedNodeMap。

如果在文檔中設置了屬性值,則 specified 屬性返回 true.

是否是avalon的HTML指示 "ms-"開頭

如果還指定了參數

能找到對應的處理函數

bindings 保存參數

 

4. 執行綁定 executeBindings

avalon.scan

找到對應的類型的bindingHandlers方法,傳入數據與vm對象,實現處理

移除數據綁定,防止被二次解析

 

5. 掃描子節點 scanNodes

avalon.scan

其實就循環處理子節點列表了,注意要過濾空文本類型

如果是元素節點就遞歸循環scanTag方法

如果是文本節點就scanText

 

6. 掃描文本 scanText

avalon.scan

 

7.抽出文本綁定 extractTextBindings

avalon.scan

文本解析是個比較復雜的東西,可以匹配很多種情況,所以需要加入很多解析的規則

scanExpr 就是掃描的表達式的匹配

documentFragment 先把這個結構讓到文檔碎片中,性能處理

 

8. 表達式匹配scanExpr

avalon.scan

代碼很長,但是處理的東西確很簡單的

比如:

"{{ w }} x {{ h }}" 一個插值表達式,那么應該如何解析

分析這個表達式,解析可以分三塊
1.  {{ w }} 

2    x 

3   {{ h }}

左右兩邊都是vm視圖所有關聯的屬性,中間x就是求值

那么解析的規則,分解3個部分,組成處理數據

tokens 就有3個組成對象

  1. expr: true
  2. filters: undefined
  3. value: " w "
  1. expr: false
  2. value: " x "
  1. expr: true
  2. filters: undefined
  3. value: " h "

解析后分解成綁定的數據

 

然后就是一次循環了, 遇到條件stopScan就終止了

 

所以總結scan無非就干了一件事,掃描到指定的行為,發送數據給處理函數

 


免責聲明!

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



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