avalon2學習教程02vm

avalon2的vm是一個很是重要的東西,其設計原型最初脫胎於knockout.js,但到avalon1.6中,終於尋得本身的方案,更精簡,更易用,更魔幻。javascript

vm是一種特殊的數據結構,看起來像普通對象,但它大部分屬性都被重寫了,從而實現「操做數據即操做視圖」的效果。咱們在定義vm時,通常須要定義$id,其次是其餘業務數據屬性,它們都是來自後端的數據表。在1.4,1.5中,還有一個叫$skipArray的數組,用於方置一些只用同步一次視圖的屬性名,這是爲了提升性能。由於將普通屬性轉換能同步視圖的特殊屬性,咱們通常稱之爲監控屬性(knockoutjs是這麼叫的),其真正術語叫訪問器屬性。此外1.4與knockout同樣,能定義計算屬性,但2.0已經廢掉,這裏就不詳述了!html

  • $id vm的ID名,用於ms-controllerjava

  • $skipArray, 數組, 用於指定那些屬性不用轉換監控屬性, 這個在定義時指定, 生成後的vm並不存在。react

var vm = avalon.define({
    $id: 'test',
    a: 11,
    b: 22
})
vm.$watch('a', function(newValue, oldValue){

})

console.log(vm)

打開控制檯,咱們還會發現vm多出一些特殊屬性,它們都是以$開頭的git

  • $events, 用於放咱們的$watch回調github

  • $fire, 用於觸發某一個屬性的全部回調chrome

  • $watch, 用於監聽某個屬性的變化,當它變化時,將對應回調依次執行後端

  • $hashcode, $id可能有重複,但$hashcode不會重複數組

  • $track, 這是一個字符串,裏面包括vm的全部屬性名(除了那些內置的$開頭屬性),以;;隔開(這用於內部對象轉換的)ruby

  • $model, 返回純淨的JS對象

  • $element, 同名的ms-controller元素節點,這是應社區的要求,怎麼經過vm獲得元素

  • $render, 靈感來自react的render方法,用於生成對應的虛擬DOM樹

  • $accessors, 儲存全部監控屬性的定義,這在avalon.modern及avalon.next不存在,avalon.modern能夠經過 Object.getOwnPropertyDescriptor獲得訪問器屬性的定義,而avalon.next是使用Proxy實現vm,徹底沒有這方面的必要。

一般咱們把avalon.define建立的vm叫頂層vm,內部使用masterFactory生成。

若是一個vm的屬性 也是一個對象,那麼它也會轉換爲vm,叫子級vm,或子vm,內部使用slaveFactory生成。

var vm = avalon.define({
    $id: 'test',
    a: 11,
    b: {
       c: 22
    }
})

console.log(vm.b)

vm.b就是一個子vm,它與頂層vm有些區別,首先其$id爲頂層vm的$id加上其屬性名構成, 即"test.b"。它少了一些系統屬性,如$element, $render, $watch, $fire, $events(這個在avalon.next存在),能夠說是一個輕量的vm。它的數據發生改動時,它不會本身處理$watch回調,而是交由頂層的vm來處理,由於全部回調都放在頂層vm的$events上。

var vm = avalon.define({
   $id: 'test',
    a: 11,
    arr: [{b:1},{b:2},{b:3}]
})
console.log(vm.arr)

若是vm的子級屬性是一個數組,那麼與1.4同樣,轉換爲監控數組。監控數組就是一個push, unshift, splice, pop, shift, sort, reverse等方法被重寫的數組。它在內部是由arrayFactory方法生成的。

若是監控數組的每一個元素是一個對象,那麼它們會轉換爲頂層vm, 由masterFactory生成,它們的$id名都叫作test.arr.*。這時大家明白$hashcode的用處了吧(如去重,排序)。

在avalon2,還提供了一個工廠來合併兩個vm

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="./dist/avalon.js"></script>
        <script>
            var vm1 = avalon.define({
                $id: "test",
                a: 111
            })
            vm1.$watch('a', function(){
                console.log('vm1.a change')
            })
            var vm2 = avalon.define({
                $id: 'test2',
                b: 222
            })
            vm2.$watch('b', function(){
                console.log('vm2.b change')
            })
            var vm3 = avalon.mediatorFactory(vm1,vm2)
            //這個回調其實放在vm1.$events中
             vm3.$watch('a', function(){
                console.log('vm3.a change')
            })
             //這個回調其實放在vm2.$events中
            vm3.$watch('b', function(){
                console.log('vm3.b change')
            })
            console.log('------')
            vm3.a = 22
            vm3.b = 44
        </script>
        <style>
            .ms-controller{
                display:none;
            }
        </style>
    </head>
    <body>
        <div ms-controller="test">
            <input ms-duplex="@a" />
            <p>{{@a}}</p>
        </div>
    </body>
</html>

在chrome控制檯中依次打印以下:

有人可能不理解爲何輸出6次,咱們先忽視調試信息。

  1. 首先前兩個是vm3.a的值發生改變,由111變成22, 因爲vm3.a實際上與vm1.a是同一個東西,所以都觸發了。

  2. 其次中間兩個是vm3.b的值發生變化,由222變成44,因爲vm3.b實際上與vm2.b是同一個東西,所以都觸發了。

  3. 最後是ms-duplex要將input.value同步爲vm1,a這時爲數字的22,但到了元素上,變成字符串的22, 因而又觸了兩下!

avalon.mediatorFactory是一個重要的方法,是實現ms-controller套嵌的關鍵,你們有興趣的話能夠看看其源碼。

頂層vm masterFactory 供用戶操做與保存回調與同步視圖
子vm slaveFactory 承載更多用戶數據
監控數組 arrayFactory 承載更多用戶數據
內部vm mediatorFactory 容納多個vm的數據與回調,並做爲參數傳入$render方法,生成新的虛擬DOM樹

與vm做用域相關的有三個指令,ms-skip, ms-controller, ms-important。

  1. ms-skip,讓vm的做用域進不到此元素內部,那麼裏面的{{}}就不會被替換了。

  2. ms-controller, 讓此vm的做用域進入此元素內部,而且若是它上方已經有ms-controller,那麼它們所指向的vm會進行合併。合併方式使用mediatorFactory實現。

  3. ms-important, 讓此vm的做用域進入此元素內部,而且屏蔽上方的ms-controller或ms-important的vm的影響。

<!DOCTYPE html>

<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="../dist/avalon.js"></script>
        <script>
            var vm = avalon.define({
                $id: "test",
                aaa: 111,
                ddd: 444
            })
            var vm2 = avalon.define({
                $id: "test2",
                ddd: 555
            })
            var vm3 = avalon.define({
                $id: "test3",
                aaa: 333
            })
        </script>
    </head>
    <body ms-controller="test">
        <p>{{@aaa}}</p>
       <div ms-controller="test2">
            {{@aaa}}::{{@ddd}}
        </div>
         <div ms-important="test3">
            {{@aaa}}::{{@ddd}}
        </div>
    </body>
</html>

最後請你們點星加贊

相關文章
相關標籤/搜索