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>

最后请你们点星加赞

相关文章
相关标签/搜索