zoukankan      html  css  js  c++  java
  • avalonJS-源码阅读(三) VMODEL

     avalon的重头戏。终于要到我最期待的vmodel了。 ps:这篇博文想做的全一点,错误少一点,所以会有后续的更新在这篇文章中。 状态:一稿
    
    目录[-]
    avalon dom小结
    数据结构
    观察者模式
    依赖收集与触发
    avalon Observable
    avalon modelFactory
    loopModel
    函数介绍
    isEqual
    小记
    avalon dom小结
    
    看过前面三篇文章后,应该会对avalon关于dom的处理有个大体的理念。这里再理一遍:avalon通过手动触发scan函数来遍历dom。然后根据ms-import ms-container ms-include ms-skip确
    
    定VMODELS的作用域,接下来便是处理用户代码并生成相应的函数,通过registerSubscriber函数,将dom生成的函数以及相应的关联数据进行注册。
    剩下的,就是其他模块的事情了。
    数据结构
    
    如常
     
    //VMODULE 严格意义上,这个不算数据结构,而且,这是object一层的结构,如果object是嵌套式的
    // {
    //     a:{
    //         b:"嵌套"
    //     }
    // }
    // 那么这个比较复杂了
    {
        $id:$string,//define函数的第一个参数,如果是嵌套层的,则随机
        $model:$obj,//define定义中的第二个参数中的vm。如果是嵌套层的,则$model为当前嵌套层及以下的值
        $watch:$fn,
        $unwatch:$fn,
        $fire:$fn,//以上三个是Observable对象下的三个方法,不过他们的上下文被修改了。
        $skipArray:true|$array,//要忽略的参数
        hasOwnProperty:$fn,//重写的方法,将其作用域定义到$model
        $accessors:$obj,下文将详细讲解。
        $event:$obj,//记录用户定义的$watch
        get ...:$fn,
        set ...:$fn,
        ...:...//经过defineProperty处理的model值
    }
    //$accessors,
    {
        $modelName:$fn //$fn[subscribers] =["记录订阅者"]
        $vmodel:$obj// 嵌套层时,出现
    }
    观察者模式
    
    在讲解 avalon vmodel之前,一定要对观察者模式有个清晰的了解,观察者模式又叫订阅发布模式(Subscribe/Publish),具体讲解参见观察者模型。
    在avalon中,作者用了两遍观察者模型,分别解决了model和view的交互和扩展用户监控model值的问题。具体实现模块为依赖收集与触发和Observable。和java代码中实现的观察者不同的
    
    是,被观察者会创建一个数组,数组内存放着所有的观察者,而观察者则都是函数。
    当被观察者产生改变时,观察者函数则会被附加上下文环境后,依次执行。
    依赖收集与触发
    
    这里先上下源码,用来做辅助讲解。
     
        
        function registerSubscriber(data) {
            Registry[expose] = data //暴光此函数,方便collectSubscribers收集
            avalon.openComputedCollect = true
            var fn = data.evaluator
            if (fn) { //如果是求值函数
                if (data.type === "duplex") {
                    data.handler()
                } else {
                    data.handler(fn.apply(0, data.args), data.element, data)
                }
            } else { //如果是计算属性的accessor
                data()
            }
            avalon.openComputedCollect = false
            delete Registry[expose]
        }
     
        function collectSubscribers(accessor) { //收集依赖于这个访问器的订阅者
            if (Registry[expose]) {
                var list = accessor[subscribers]
                list && avalon.Array.ensure(list, Registry[expose])//只有数组不存在此元素才push进去
            }
        }
    
    上面代码中,accessor函数的[subscribes]属性,做了被观察者存储观察者的事情。
    为什么registerSubscriber函数就敢肯定在 Registry[expose] = data和delete Registry[expose]之间,collectSubscribers会被执行?
    秘密就在于descriptorFactory函数对Object.defineProperty函数的应用。我们先写一点Object.defineProperty的简单例子,具体理解可参考数据属性和访问器属性。
     
        
    var a={};
    Object.defineProperty(a,"a",{
         
        get:function(){
            console.log("get a")
            return "默认值"
        },
        set:function(val){
            console.log("set a")
            value=val;
        }
    });
    console.log(a.a);
    console.log(a.a="new value")
    console.log(a.a);
    // get a 
    // 默认值 
    // set a 
    // new value 
    // get a 
    // 默认值 
     
    var memeryValue="默认值";//we need to save the value in some where
    Object.defineProperty(a,"b",{
        get:function(){
            console.log("get b")
            return memeryValue
        },
        set:function(val){
            console.log("set b")
            memeryValue=val;
        }
    });
    console.log(a.b);
    console.log(a.b="new value")
    console.log(a.b);
    // get b
    // 默认值
    // set b
    // new value
    // get b
    // new value
    
    通过上面的例子,结合观察者模型我们可知道,通过对get/set的设定,我们可以观察用户什么时候对vmodel里绑定的值进行赋值或读取,并做相应的处理(对avalon来讲,就是调用
    
    accessor函数)。
    至于notifySubscribers函数,则是调出绑定在accessor函数上的观察者集合,并执行。
    avalon Observable
    
    Observable是一个对象。它下面定义了三个方法,分别为$watch、$unwacth、$fire。这三个方法会和$events会被注入到每一个用户定义的vm中(如果用户定义的vm是嵌套的,方法会在每
    
    个嵌套层都注入一下)。用户只需要了解$watch和$unwacth用法即可,fire交给avalon自行处理就好了。
    $events用来记录观察者和被观察者关系。
    
    注意:"$events"下面的属性会多出类似这样的值{"modelValue":undefined},而这个值的来源计算属性
    accessor的真么一段代码。
         
    //name='abc'
    var backup = vmodel.$events[name]//当vmodel.$events[name]不存在时,backup会被赋值为undefined
    vmodel.$events[name] = []
    setter.call(vmodel, newValue)
    vmodel.$events[name] = backup//这时,{abc:undefined}
    
    这个地方好生纠结,自己稍微改了一下:
     
        
    var backup = vmodel.$events[name]
    vmodel.$events[name] = []
    setter.call(vmodel, newValue)
    if(backup===undefined)
        delete vmodel.$events[name]
    else    
        vmodel.$events[name] = backup
    
    avalon modelFactory
    
    modelFactory由两个重要的函数构成 loopModel和descriptorFactory。
    loopModel
    
    在说loopModel之前,我们要先了解下avalon的5种属性。
    
        model属性,存放着未被avalon处理用户定义的属性集合。你可以把它当成java的entity,和后台进行数据交互时,直接和它进行交互,avalon会自动触发订阅。
    
        normalProperties 普通属性,不需要双向绑定的,例如放在$skipArray里的属性,用户自定义以$开头的,函数以及avalon自定义的一些函数($event, $watch 等)。
    
        accessingProperties 监控属性,要进行双向绑定的属性。
    
        watchProperties 强制要监听的属性,以$开头的,但又想强制监听它,例如avalon内部定义的$event等。这个属性是normalProperties的补充,属于内部属性,用户不会使用到它。
    
        computedProperties 计算属性,用户自己自定义的set和get方法。是accessingProperties的补充。
    
    loopModel函数的主要作用是将用户定义的vm object进行属性归类,并生成被观察者函数accessor。
    作为被观察者accessor,他需要绑定来自dom的观察者到自身上以及将值大的改变通知到来自dom的观察(notifySubscribers)和来自用户自定义的观察(safeFire)。依据
    
    accessingProperties、computedProperties的嵌套的的不同特点构造了三种类型的accessor。
    我们捡一个对object嵌套的accessor实现来看看。他除了实现上面的功能外,还需要调用modelFactory对嵌套的每一层生成VMODULE结构,在这个实现上,为了尽可能的复用现有代码,牺牲
    
    了数据结构,avalon.vmodels关于$model的记录有些重复。至于updateWithProxy、updateVModel函数以及关于的详细讲解,会在以后补充上。
    函数介绍
    isEqual
     
        
    var isEqual = Object.is || function(v1, v2) {
            if (v1 === 0 && v2 === 0) {
                return 1 / v1 === 1 / v2
            } else if (v1 !== v1) {
                return v2 !== v2
            } else {
                return v1 === v2
            }
        }//虽然看不懂,但不妨碍使用
    小记
    
    avalon的双向绑定的基本内容就这么多了。代码读到现在终于有一种可解脱的感觉了。感谢司徒正美给我们带来如此优秀的代码,另推荐他的一本书《javascript框架设计》,虽然校验的
    
    不怎么样,小bug不断,但内容绝对丰满,适合低中级的Jser反复阅读(至于高级适不适合,我就不知道了)。
  • 相关阅读:
    java的final变量理解
    springMVC如何判断入参是默认参数还是请求传过来的参数?
    java反射新的应用
    冒烟测试和回归测试的区别
    git变基、冲突解决
    mockServer学习
    mongodb查询之模糊查询
    springxml配置构造函数入参
    mockito学习
    solr官方文档翻译系列之schema.xml配置介绍
  • 原文地址:https://www.cnblogs.com/kubimiantiao/p/4303254.html
Copyright © 2011-2022 走看看