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反复阅读(至于高级适不适合,我就不知道了)。
  • 相关阅读:
    共享纸巾更换主板代码分析 共享纸巾主板更换后的对接代码
    Python Django Ajax 传递列表数据
    Python Django migrate 报错解决办法
    Python 创建字典的多种方式
    Python 两个list合并成一个字典
    Python 正则 re.sub替换
    python Django Ajax基础
    Python Django 获取表单数据的三种方式
    python Django html 模板循环条件
    Python Django ORM 字段类型、参数、外键操作
  • 原文地址:https://www.cnblogs.com/kubimiantiao/p/4303254.html
Copyright © 2011-2022 走看看