• avalon2学习教程15指令总结


    avalon的指令在上一节已经全部介绍完毕,当然有的语焉不详,如ms-js。本节主要总结我对这方面的思考与探索。

    MVVM的成功很大一语分是来自于其指令,或叫绑定。让操作视图的功能交由形形式式的指令来代劳。VM,成了一个大管家。它只一个反射体。我们对它的操作,直接影响到视图。因此俗称“操作数据即操作视图”!至于它是怎么影响视图,avalon视其版本的不同,也有不同的解法。如果抛开avalon,纵观世上所有MVVM框架,大抵有如下几种方式

    1. 函数wrapper:将原数据对象重新改造,所有属性都变成一个函数,有参数时就是赋值,进行视图同步与回调派发,没有参数时就取值,进行依赖收集。如knockout.js。
    2. 上帝getter,setter: 将原数据对象重新包装,但对数据的操作必须经过统一的set,get方法。在set方法进行视图同步与回调派发,没有参数时进行依赖收集。如reactive.js。如果放松要求,react也是这种方式,它使用setState进行视图同步。但它们依赖收集的过程。
    3. 函数编译及脏检测:将VM放到一个函数体内,取toString重新编译,内部是第一种方式。如angular.
    4. Object.defineProperty属性劫持:前三种的用户体验非常糟,于是有了这种方式,最初我是从emberjs中学来的。avalon,vue及其他后来的MVVM框架都是使用这种方式。在赋值时执行内部的setter方法,进行视图同步与回调派发,在取值时执行内部的getter方法,进行依赖收集。
    5. Object.observe对象监控: 这个API很短命,因此没几个MVVM框架用上它。由于无法进行依赖收集,需要别辟蹊径!
    6. Proxy对象监控:Object.observe对用户的行为监控是很弱的,并且是异步的,不够友好。于是有了这个新宠物。但这其实也不算新宠,firefox4就存在了。它能对数据赋值,取值,遍历,删除等各种行为都能监控到,了解Object.defineProperty 不能监控新属性的难题。在avalon2 中,就有一分支使用它实现。avalon2一共使用了VBScript, Object.defineProperty, Proxy实现vm。并且它没有进行依赖数据,而是将整个视图编译成一个大函数,每次数据变动,都重新执行这个函数,产生虚拟DOM,前后虚拟DOM进行diff,最后全量更新。这思路从react.js学来的。

    上面说了,既然使用编译整个视图成模板函数这一手段,我们就尽量让这函数轻量化。位于这视图上的所有指设也要简化,方便在对应位置上代入VM中的属性。在avalon1及其他MVVM框架,都是使用动态依赖收集方式来推断指令中的某个单词是否为vm中的某个属性,这性能耗损比较严重。在avalon2直接让用户在属性名前加上 @ 符号,人工优化这步骤了。此外,指令属性值的设计原则也很明确,就是方便转换一个函数,返回对象或对象数组(如ms-attr,ms-css,ms-widget,ms-effect,ms-class,ms-hover,ms-active,ms-for),少量的返回布尔或字符串等直接可用于JS 语句的字面量。除了ms-for与过滤器,没有其他特殊语法。

    图片描述
    除了ms-duplex,ms-on,原则上不再出来ms-xxx-yyy这样的指令。ms-后面只跟一个单词就够了,不用再加-及其他单词。

    各种指令的优先级如下:
    ms-for, ms-widget, ms-effect, ms-if…………其他指令(按指令名的charCodeAt排序)…………ms-duplex!

    ms-duplex是最后,因此再不用担心它与其他指令冲突的问题。

    指令的更新时机有两个,一个是位于此标签之间的所有孩子执行之前,一个是位于此标签之间的所有孩子执行之后。亦即change, afterChagne列队。有兴趣的话,可以阅览这里的源码

    想自定义指令,可以使用avalon.directive方法,第一个为指令名,第二个是定义体,里面至少有parse, diff, update三个方法。自己参看css指令,编写指令吧。

    //css指令
    
    var update = require('./_update')
    
    avalon.directive('css', {
        parse: function(cur, pre, binding) {
           cur[binding.name] = avalon.parseExpr(binding)
        },
        diff: function (copy, src, name) {
            var a = copy[name]
            var p = src[name]
            if (Object(a) === a) {
                
                a = a.$model || a//安全的遍历VBscript
                if (Array.isArray(a)) {//转换成对象
                    a = avalon.mix.apply({}, a)
                }
                if (typeof p !== 'object') {//如果一开始为空
                    src.changeStyle = src[name] = a
                } else {
                    var patch = {}
                    var hasChange = false
                    for (var i in a) {//diff差异点
                        if (a[i] !== p[i]) {
                            hasChange = true
                            patch[i] = a[i]
                        }
                    }
                    if (hasChange) {
                        src[name] = a
                        src.changeStyle = patch
                    }
                }
                if (src.changeStyle) {
                    update(src, this.update)
                }
            }
            delete copy[name]//释放内存
        },
        update: function (dom, vdom) {
            var change = vdom.changeStyle
            var wrap = avalon(dom)
            for (var name in change) {
                wrap.css(name, change[name])
            }
            delete vdom.changeStyle
        }
    })
    
    
    

    里面的parse(cur, pre, binding)方法是用于创建虚拟DOM, cur是通过vm.$render方法生成的新虚拟节点,pre是之前的虚拟节点,binding是当前指令抽象生成的绑定对象。

    里面的diff(copy, src, name)方法是用来比较前后两个虚拟DOM。copy是新虚拟节点,src是之前的虚拟DOM,name为指令的名字。当你用各种方式比较出这两个虚拟DOM有差异,那你就可以使用require('./_update')这个方法执行更新,更新方式为指令的update方法。

    2.1.0后,刷新机制有点改动,两个节点比较出差异后立即更新真实DOM, 不像过去那样全部比较再全量更新。

    里面的update(dom,vnode,parent)方法是用来更新真实元素的。

    最后你可以在avalon.directives对象中指到所有指令的定义。你也可以在vm.$element.vtree中看到你生成的虚拟DOM树。

    既然avalon的指令已经全部介绍完了,因此大家现在可以直接使用avalon2了!

  • 相关阅读:
    [CodeForces
    [CodeChef]RIN(最小割)
    [Bzoj3894]文理分科(最小割)
    [Poj3469]Dual Core CPU(最小割)
    MySQL- 锁(3)
    MySQL- 锁(1)
    MySQL- 锁(2)
    MySQL-中文全文检索
    Solr
    多线程编程-之并发编程:同步容器
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/5621381.html
Copyright © 2020-2023  润新知