zoukankan      html  css  js  c++  java
  • 迷你MVVM框架 avalonjs 学习教程3、绑定属性与扫描机制

    在MVVM框架中,你都会看到页面定了许多奇怪的属性,比如knockout的data-☆,angular的ng-☆,avalon的ms-☆,此外还有一些只写文本节点上的双花括号,它们统称为指令。ms-☆由于定义在元素节点上,是一个特性节点(Attribute),因此称为绑定属性。 双花括号称之为插值表达式,意即这里在插入ViewModel对应的属性,或通过加减乘除后得到的结果。

    绑定属性与插值表达式对于MVVM是非常重要的东西,它们是实现双向绑定的重要一环,我们通过它来操作DOM。在angular里面,还可以自定义标签,不过自定义标签在IE6-8下存在兼容性问题,因此被avalon抛弃了。knockout还有一些特殊的注释节点做指令,这在UI,OL,LI标签间会引发各种渲染BUG,这得需要大量代码来修复,因此avalon也不欢迎它。avalon使用最可靠的特性节点与文本节点的内容做指令,使其兼容性最好。

    avalon的许多指令从是knockout抄过来的,如ms-visible, ms-click, ms-if, ms-with, ms-each……angular兴起后,也抄来了它的ms-repeat, ms-include,ms-include-src,{{}}。但ms-on-*这种接第三个参数的设计是从rivetsjs抄来的。

    先说绑定属性,它的名字是分为三部分。由于它是特性节点,根据HTML规范,它只能是全小写,就算有大写,浏览器也会将它转换成小写,这个大家要注意了。每一部分是由“-”隔开,最开始是前缀,avalon的前缀是ms,意即mass, 弥撒,记念我之前的框架mass Framework。正因为搞了mass Framework,我获得了像jQuery那样强大的DOM处理能力,掌握大量浏览器私有方法与属性,成吨的黑魔法与飞线方案。换言之,技术是需要积累,每个框架都存在传承关系。

     <body AAA="aaa"> 
          test
      </body>
    

    enter image description here enter image description here 第二个是指令的名字,如if、visible、on、text、html、include……从它们的名字,大家也能一窥端睨。如if表示是否它输出到页面,visible表示是否可见,on是绑定事件,text是原样输出,html是转换为HTML标签再输出,include表示加载子模板到当前位置。avalon就是根据它转换为不同的视图刷新函数。

    第三个是指令的参数,比如 我们可以ms-on-click,ms-on-keyup,ms-duplex-radio,ms-duplex-bool,ms-duplex-text。此外,还有一个特殊的东西,如ms-click-1,ms-click-2,ms-click-3表示可以为某一个元素绑定N个点击事件。这些我们以后会慢慢详述。而绑定属性的值,则复杂多了,不过具体来说,表示事件的要求对应一个回调的名字,后面可带小括号可不带;表示字符串属性(ms-title, ms-value,ms-src, ms-href, ms-alt)的,里面可以添加插值表达式;与类名相关的ms-class, ms-hover, ms-active可以跟一个冒号,通过它之后表达式计算是否添加移除对应的类名。下面的avalon的绑定属性的族谱—— enter image description here

    从这图,我们也可以看出,绑定属性间也是存在继承关系的,或者准确地说,一些绑定属性是从某个绑定属性衍生出来的。最明显的是ms-click、ms-keyup、ms-mousedown都是从ms-on-☆衍生出来。它们在框架内部是调用同一方法。

     var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit")
       if (events[type]) {
        param = type
        type = "on"
    } else if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替
        type = "disabled"
        value = "!(" + value + ")"
    }
    //吃掉以下几个绑定,用ms-attr-*绑定代替
    if (type === "checked" || type === "selected" || type === "disabled" || type === "readonly") {
        param = type
        type = "attr"
        elem.removeAttribute(name)
        name = "ms-attr-" + param
        elem.setAttribute(name, value)
        match = [name]
        msData[name] = value
    }
    if (typeof bindingHandlers[type] === "function") {
        var binding = {
            type: type,
            param: param,
            element: elem,
            name: match[0],
            value: value,
            priority: type in priorityMap ? priorityMap[type] : type.charCodeAt(0) * 10 + (Number(param) || 0)
    }              
    

    我们细看源码,就会发现,一个绑定属性会在内部转换为一个叫binding的对象,其属性名的第二个部分,如if, visible变成它的type,第三部分变成param,绑定属性所在的元素节点就是它的element,原属性值就是value,原属性名则是name,此外还有优先级priority。这就涉及avalon另一个重要的机制了——扫描机制

    绝对大多数MVVM框架是没有自己的选择器引擎的,那么它怎么找到要处理的元素呢?办法就在于框架在DOMReady之时,对DOM树进行全盘扫描,把特殊的标签,如它有特殊的tagName,有绑定属性,有插值表达式的元素,全部保存起来。avalon里就有一个scan方法,它有两个可选参数,一个是元素节点,另一个是ViewModel,也可以是ViewModel数组。顺序是从上到下扫描,页面必须要有ms-controller, ms-important绑定属性。如果没有这两个绑定属性,就算你写了ms-visible,ms-text,它们也不会生效,因为它们找不到自己的作用域对象(ViewModel)。

    扫描机制一般是从body元素开始,逐等下级,它会跳过script, style, noscript, textarea这几个元素的内部。而script、noscript、textarea我通常称之为模板元素,因为它们在avalon都用于存放子模板。

    一个元素可以绑定多个指令,这些指令存在优先级,其中ms-skip的优移动级最高(0),其次是ms-important(1),再次是ms-controller(2),它们决定扫描引擎是否继续往下扫描,因此排最前面。

    其他元素可以从内部一个哈希里查到,越小的优先级越高。

    var priorityMap = {
        "if": 10,
        "repeat": 90,
        "widget": 110,
        "each": 1400,
        "with": 1500,
        "duplex": 2000,
        "on": 3000
    }
    

    可以看到排第4的是if,排第5的是repeat,排第5的是widget,那么它们与each之间的其他属性呢?ms-attr,ms-visible, ms-data的优先级是怎么计算出来的呢?就要用到下面这条公式了:

    type.charCodeAt(0) * 10 + (Number(param) || 0)
    

    比如ms-data-index,它的type为data,取其第一个字母d,再取其UniCode值,计算得到100, 再乘以10, 得1000,然后取其第三个参数index强制转数字,得到NaN,后跟短路或于是变成0,最后相加,等到1000。这就是它的优先级了。

    由于属性名只能是小写,因此就是z开头,最大也只能是1220上下。最后计算得到它们的移动后顺序为:

    ms-skip(0) --> ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(90) -->ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)-->ms-duplex(2000)-->ms-duplex(3000)垫后

    有关扫描顺序更详细的介绍可见这里,不过用户不必在意它们,框架这样巧妙的设计已经保证你为同一个元素绑定N个指令,它们都能相安无事地正常执行。

    最后我们看一下绑定属性能为我们做什么吧,这里有一个图,展示了它们的所有功能。下一章节起来我们就根据它逐个击破了。

    enter image description here

    <!DOCTYPE html>
    <html>
        <head>
            <title>ms-duplex</title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge" /> 
            <script src="avalon.js" ></script>
            <script>
                var model = avalon.define("model", function(vm) {
                    vm.textModel = "text";
                    vm.passwordModel = "password"
                    vm.radioModel = true;
                    vm.checkRadio = true;
                    vm.selectModel = "bbb";
                    vm.checkboxModel = ["aaa", "bbb"];
                    vm.checkboxText = vm.checkboxModel.join(",")
                    vm.sex = "1"
    
                });
                var qinerg = avalon.define("qinerg", function(vm) {
                    vm.sex = "";
                    vm.lang = []
                    vm.langtext = ""
                });
                qinerg.lang.$watch("length", function() {
                    qinerg.langtext = qinerg.lang.join(",")
                })
                model.checkboxModel.$watch("length", function() {
                    model.checkboxText = model.checkboxModel.join(",")
                })
                var dynamic = avalon.define("dynamic", function(vm) {
                    vm.langtext = ""
                    vm.array = ["aaa", "bbb", "ccc"]
                    vm.lang = []
                })
    
                dynamic.lang.$watch("length", function() {
                    dynamic.langtext = dynamic.lang.join(",")
                })
    
            </script>
            <style>
                .parent{
                    height:50px;
                    100%;
                    overflow:hidden;
                }
                .visible{
                    400px;
                    height: 50px;
                    background: red;   
                    float:left;
                }
                .if{
                    400px;
                    height: 50px;
                    background: blueviolet;  
                    float:left;
                }
                fieldset{
                    background:#d2d2d2;
                }
    
            </style>
        </head>
        <body >
            <div ms-controller="model">
                <h3 style="text-align: center">ms-duplex</h3>
                <input ms-duplex="textModel" ms-data-duplex-observe="radioModel"/>
                <input ms-duplex="passwordModel" type="password"/>
    
                <input type="radio" ms-duplex="radioModel"> <input type="checkbox" ms-duplex-radio="checkRadio">
                <select ms-duplex="selectModel">
                    <option value="aaa" selected>aaa</option>
                    <option value="bbb">bbb</option>
                    <option value="ccc">ccc</option>
                </select>
                <input ms-duplex="checkboxModel" type="checkbox" value="aaa" />
                <input ms-duplex="checkboxModel" type="checkbox" value="bbb" />
                <input ms-duplex="checkboxModel" type="checkbox" value="ccc" />
                <input ms-duplex-text="sex" type="radio" value="1"/>
                <input ms-duplex-text="sex" type="radio" value="2"/>
                <input ms-duplex-text="sex" type="radio" value="3"/>
                {{sex}}
                <p>text, password, textarea要求对应的VM属性为字符串,
                    select、checkbox为字符串数组,radio为布尔,
                    我们可以通过ms-duplex-radio让checkbox对应一个布尔,
                    ms-duplex-text让radio对应一个字符串 </p>
                <div class="parent">
                    <div ms-visible="radioModel" class="visible">
                        <div>ms-visible这个区域是受到radioModel控制</div>
                        <div>data-duplex-observe为{{radioModel}}</div>
                    </div>
                    <div ms-if="checkRadio" class="if">
                        <div> ms-if这个区域是受到checkRadio控制</div>
                    </div>
                </div>
                <fieldset>
                    <legend>textModel</legend>
                    <p>{{textModel}}</p>
                </fieldset>
                <fieldset>
                    <legend>passwordModel</legend>
                    <p>{{passwordModel}}</p>
                </fieldset>
                <fieldset>
                    <legend>radioModel</legend>
                    <p>{{radioModel}}</p>
                </fieldset>
                <fieldset>
                    <legend>selectModel</legend>
                    <p>{{selectModel}}</p>
                </fieldset>
                <fieldset>
                    <legend>checkboxModel</legend>
                    <p>{{checkboxText}}</p>
                </fieldset>
    
    
            </div>
            <div ms-controller='qinerg' >
                <p><input type="radio" ms-duplex-text="sex" value="man">男
                    <input type="radio" ms-duplex-text="sex" value="woman">女</p>
                <p>
                    <input type="checkbox" ms-duplex="lang" value="#C">#C
                    <input type="checkbox" ms-duplex="lang" value="java">java
                    <input type="checkbox" ms-duplex="lang" value="ruby">ruby
                </p>
                <P>{{sex}}                   {{langtext}}</P>
            </div>
            <fieldset ms-controller="dynamic">
                <div  ms-each="array">
                    <div><input type="checkbox" ms-duplex="lang" ms-value="{{el}}">{{el}}</div>
                </div>
                <div>{{langtext}}</div>
            </fieldset>
    
        </body>
    </html>
    

    enter image description here

  • 相关阅读:
    Java实现简易聊天室
    Jnetpcap简述
    Win10系统配置Java环境变量
    截取HTML中的JSON数据并利用GSON进行解析(Android)
    Android实现监听控件点击事件
    Android实现点击两次返回退出APP
    简述RadioGroup和RadioButton的使用
    服务的最佳实践--完整版的下载示例
    使用HTTP协议访问网络(Android)
    补充1:IDA的脚本IDC语言
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/3966074.html
Copyright © 2011-2022 走看看