• 迷你MVVM框架 avalonjs 入门教程


    迷你MVVM框架 avalonjs 入门教程

    1. 关于AvalonJs
    2. 开始的例子
    3. 扫描
    4. 视图模型
    5. 数据模型
    6. 绑定属性与动态模板
    7. 作用域绑定(ms-controller, ms-important)
    8. 模板绑定(ms-include)
    9. 数据填充(ms-text, ms-html)
    10. 类名切换(ms-class, ms-hover, ms-active)
    11. 事件绑定(ms-on,……)
    12. 显示绑定(ms-visible)
    13. 插入绑定(ms-if)
    14. 双工绑定(ms-duplex,原来的ms-model)
    15. 样式绑定(ms-css)
    16. 数据绑定(ms-data)
    17. 布尔属性绑定(ms-checked, ms-selected, ms-readonly, ms-disabled, ms-enabled)
    18. 字符串属性绑定(ms-title, ms-src, ms-href……)
    19. 万能属性绑定(ms-attr)
    20. 万能绑定(ms-bind)
    21. 循环绑定(ms-each)
    22. UI绑定(ms-ui)
    23. $watch
    24. 过滤器
    25. AMD加载器

    关于AvalonJS

    avalon是一个迷你的MVVM框架,虽然从发布到现在,它臌胀了不少,但它现在还是比knockout小许多。avalon开发过程一直遵循三个原则:1,复杂即错误,2,数据结构优于算法,3,出奇制胜。这三大原则保证avalon具有良好的维护性,扩展性,与众不同。

    简单说一下其他三大MVVM的实现思路:

    1. knockout:最早冒出来的JS MVVM库,通过转换VM中所有要监听的东西为函数,然后执行它们,得到某一时刻中,一共有多少函数被执行,将它们放到栈中,最底的就是最先被执行的,它上面的就是此函数所依赖的函数,从而得到依赖关系。 然后设计一个观察者模式,从上面的依赖检测中,将依赖函数作为被依赖者(最先执行的那个的)的订阅者,以后我们对被依赖者进行赋值时,就会通先订阅者更新自身,从而形成一个双向绑定链。 并且,knockout会将视图中的绑定属性进行转换,分解出求值函数与视图刷新函数,视图刷新函数依赖于求值函数,而求值函数亦依赖于我们VM中的某些属性(这时,它们都转换为函数),在第一次扫描时,它们会加入对应属性的订阅者列队中, 从而VM中的某个属性改变,就会自动刷新视图。
      评价:实现非常巧妙,是avalon0.1-0.3的重要学习对象,但将属性变成一个函数,让人用点不习惯,许多用法都有点笨笨的。 虽然是一个轻盈的库,但扩展性不强,里面的实现异常复杂,导致能参与源码的人太少。
    2. emberjs: 一个大而全的框架,包罗万象。一开始是使用Object.defineProperty+观察者实现,但IE8的问题,让它不得不启用上帝setter, 上帝getter。没有自动收集依赖的机制,没有监控数组,计算属性需要自己指定依赖。VM可继承。 VM与视图的双向绑定依赖于其强大无比上万行的Handlebars 模板。听说是外国目前最好用的MV*框架。因为作者既是jQuery的核心成员,也是Rails的核心成员,虽然由于技术能力没实现自动收集依赖,但框架的其他方面做得非常易上手,人性化。 
      评价:太大了,优缺点同python的Django框架。
    3. angular: google组织开发的框架,体现其算法至上的时候到了。里面一共有两个parser, 一个是ngSanitize/sanitize.js下的HTML parser, 一个是ng/parse.js(它要配合compile.js使用)的JS parser。第一个parser负责绑定抽取,第二个负责从Ctrl函数,工厂函数,服务函数及$watch回调中分解出无数setter, getter, 确认它们的依赖关系,放进观察者模式中。它的观察者无比强大,由于它的VM能继承,于是通过继承链实现四通发达的消息广播。它还实现了一个基于LRU的缓存系统,因为google最喜欢以空间换时间了,另一方面说明它要缓存的东西太多了,非常吃内存。 公司内部用angular实现的grid,200行在PC中就拖不动了。它还用到许多时髦的东东,如HTML5 history API, 迷你版Q Promise。内部是极其复杂。 不过最大的问题是,它是基于parser,静态编译,这意思着什么呢?不抗压缩!为了,它引进了IOC,官网上给出的简单例子其实在项目完全不可用,我们需要使用另一种更复杂的写法,方便编泽器从它们得到不被压缩的部分, 让它在压缩情况也能正常运行。由于基于编译,许多行为都不是即时的,可预见的。用户写的那些控制器函数,都是为编译做准备。由于基于编译,它不得不要求我们对具有兼容问题的一些全局函数,方法进行屏蔽,用它的给出的服务替代它们,如 window对应$window, document对应$document, location对应$location, setTimout对应$timeout……如果不遵循这规则,它可能运行不了,你需要手动使用$digest手动触发。 不过对于一些复杂的回调,$digest也奈何不了,但又不报错,基本无法调试,只能撞大运般地一点点改……
      评价:非常恶心的框架,是google继于GWT、Closure、Dart发明的又一垃圾 !

    现在的avalon是我在完全消化了knockout发展起来的,准确来说,是0.4版,通过Object.defineProperties与VBScript实现了与普通对象看起来没什么两样的VM,VM里面充满了访问器属性,而访问器属性肯定对应一个setter,一个getter, 我们就在setter, getter中走knockout的老路,实现自动收集依赖,然后放进一个简单的观察者模式中,从而实现双向绑定。将绑定属性分解为求值函数与视图刷新函数,早前,avalon也与knockout一样使用一个简单的parser,然后通过with实现, 0.82一个新的parser 上马,同样的迷你,但生成的求值函数,更方便依赖收集,并且没有with语句,性能更佳。angular也不是一无是处,我也从它那里抄来了{{}}插值表达式,过滤器机制,控制器绑定什么的。

    avalon在内部使用了许多巧妙的设计,因此能涵盖angular绝对大多数功能,但体积却非常少。此外,在性能上,现在除了chrome外,它都比knockout快,angular则是最慢的。 在移动端上,avalon这个优势会被大大放大化的。

    关于avalon的几点:

    • 兼容IE6
    • 没有AJAX与动画模块,需要配合jQuery等库使用
    • avalon会自动同步视图,因此不要在VM中进行DOM操作

    迷你MVVM框架在github的仓库https://github.com/RubyLouvre/avalon, 如果你要兼容IE6,那么下其中的avalon.js, 如果你只打算兼容IE10与标准浏览器,那么下avalon.mobile.js。

    官网地址http://rubylouvre.github.io/mvvm/

    开始的例子

    我们从一个完整的例子开始认识 avalon :

    <!DOCTYPE html>
    <html>
        <head>
            <title></title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script src="avalon.js"></script>
        </head>
        <body>
            <div ms-controller="box">
                <div style=" background: #a9ea00;" ms-css-width="w" ms-css-height="h"  ms-click="click"></div>
                <p>{{ w }} x {{ h }}</p>
                <p>W: <input type="text" ms-model="w" data-event="change"/></p>
                <p>H: <input type="text" ms-model="h" /></p>
            </div>
            <script>
                avalon.define("box", function(vm) {
                    vm.w = 100;
                    vm.h = 100;
                    vm.click = function() {
                        vm.w = parseFloat(vm.w) + 10;
                        vm.h = parseFloat(vm.h) + 10;
                    }
                })
            </script>
        </body>
    </html>

    上面的代码中,我们可以看到在JS中,没有任何一行操作DOM的代码,也没有选择器,非常干净。在HTML中, 我们发现就是多了一些以ms-开始的绑定属性与{{}}插值表达式,有的是用于渲染样式, 有的是用于绑定事件。在ms-model中,我们会发现它会反过来操作VM,VM的改变也会影响视图的其他部分。

    扫描

    不过上面的代码并不完整,它能工作,是因为框架默认会在DOMReady时扫描DOM树,将视图中的绑定属性与{{}}插值表达式抽取出来,转换为求值函数与视图刷新函数。

    我们可以通过下面方法自己扫描DOM树:

    avalon.ready(function() {
         avalon.define("box", function(vm) {
             vm.w = 100;
             vm.h = 100;
             vm.click = function() {
                 vm.w = parseInt(vm.w) + 10;
                 vm.h = parseInt(vm.h) + 10;
             }
         })
         avalon.scan()
     })

    scan有两个可选参数,第一个是扫描的起点元素,默认是HTML标签,第2个是VM对象。

    //源码
        avalon.scan = function(elem, vmodel) {
            elem = elem || root
            var vmodels = vmodel ? [].concat(vmodel) : []
            scanTag(elem, vmodels)
        }

    视图模型

    我们是通过avalon.define函数返回一个视图对象VM,并且avalon.define(vmName, function(vm){})中的vm并不等于VM,工厂函数中的vm是用于转换为VM的。生成的VM比用户指定的属性还多了许多属性。

    默认的,除了函数外,其他东西都转换为监控属性,计算属性与监控数组。如果不想让它转换,可以让此属性以 $开头,框架就不会转换它们。

        

    如果实在不放便改名,又不想被转换,比如是一个jQuery对象或一个DOM节点,如果转换,肯定拖死框架,我们可以放到vm.$skipArray = [propName1, propName2]中去,这样也忽略转换。

    另外,avalon不允许在VM定义之后,再追加新属性与方法,比如下面的方式是错误的:

                    var vm = avalon.define("test", function(vm) {
                        vm.test1 = '点击测试按钮没反应 绑定失败';
                    });
                    vm.one = function() {
                        vm.test1 = '绑定成功';
                    };
       //这里有两个错误,
       //1在命名上没有区分avalon.define的返回值与它回调中的参数,
       //2one方法的定义位置不对(这是考虑到兼容IE6-8,要求所有浏览器保持行为一致)
            

    数据模型

    当我们要用AJAX与后端交互时,如果直接把VM传上去太大了,这时我们需要把它对应的纯数组的JS对象。在VM中有个叫$model的属性,这是一个对象,就是数据模型M了。当我们更改VM时,框架就会自动同步M

    绑定属性与动态模板

    在开始之前,我们看一下静态模板是怎么工作的:

    我之前写了一个叫ejs的静态模板引擎:

    <script type="tmpl" id="table_tmpl">
            <&= title() &>
            <table border=1>
            <&- for(var i=0,tl = @trs.length,tr;i<tl;i++){  -&>
                <&- tr = @trs[i]; -&>
                <tr>
                <td><&= tr.name;; &></td> <td><&= tr.age; &></td> <td><&= tr.sex || "男" &></td>
                </tr>
                <& } &>
            </table>
            <&# 怎么可能不支持图片 &>
            <img src="<&= @href &>">
    </script>

    它是以一个script标签做容器,里面的整个叫模板。模板里面有许多以 <& 与 &>划分出来的区块,用于插入JS代码,以@开头的变量是对应于数据包中的某个属性。

    几乎所有静态模板的实现原理都是一样的,将这个模板变成一个函数,然后里面分成静态部分与动态部分,静态部分就是上面的HTNMl部分,转换为一个个字符串,动态部分就是插入的JS代码, 它们基本上原封不动地成为函数体的逻辑。然后我们传入一个对象给这个函数,最后得到一个符合HTML格式的字符串,最后用它贴到页面上某个位置就行了。

    静态模板有几个缺点,首先它容易混入大量的JS逻辑,对于菜鸟来说,他们特别喜欢在里面放入越来越多JS代码。这个在JSP年代,已经证明是bad practice。为此出现了logic-less的 mustache。 其次,它更新视图总是一大片一大片地处理,改动太大。最后,是由于第2点引发的问题,它对事件绑定等不友好,因为一更新,原来的节点都被消灭了,需要重新绑定。幸好,jQuery普及了事件代理,这问题才没有 暴露出来。

    再看动态模板,几乎所有MVVM框架都用动态模板(当然也有例外,如emberjs)。动态模板以整个DOM树为容器,它通过扫描方式进行第一次更新视图。 在静态模板,通过<& 与 &>划分的部分,转换为绑定属性与{{}}插值表达式(这是一种文本绑定,在avalon中,我们可以通过|html过滤器,转换html绑定) 这样就有效阻止用户在页面上写逻辑。虽然动态模板也支持ms-if, ms-each等表示逻辑关系的绑定,但它的值最复杂也只能是一个表达式。 在绑定属性中,属性名用于指定操作行为,如切换类名,控制显示,循环渲染,绑定事件,数据填充什么的,而属性值是决定这些操作是否执行,与渲染结果。 由于双向绑定的关系,它不像静态模板那样,每次都要自己将数据包放进函数,得到结果,然后innerHTML刷新某个区域。它是在用户为VM的某个属性进行重新赋值,将视图中对应的某个文本节点, 特性节点或元素节点的值进行重刷。因此不会影响事件绑定。

    在avalon中,这些视图刷新函数都有个element属性,保持对应的元素节点,每次同步时,都会检测此元素节点是否在DOM树,不在DOM树就取消订阅此刷新函数,节约内存,防止无效操作。

    因此,你们可以看区别了吧。绑定属性与插值表达式就是对应静态模板中的JS逻辑部分,由于只允许为表达式或单个属性值,复杂度被控制了,强制用户将它们转移到VM中。 VM作为一个数据源,对应静态模板的数据包,并且多了一个自动触发功能,进化成一个消息中心。

    <p ms-controller="test" ms-click="click">{{ a }}</p>
     
    <script>
        avalon.define("test", function(vm) {
            vm.a = '123';
            vm.click = function() {
                vm.a = new Date - 0
            }
        })
    </script>

    作用域绑定(ms-controller, ms-important)

    avalon提供ms-controller, ms-important来指定VM在视图的作用范围。比如有两个VM,它们都有一个firstName属性,在DIV中,如果我们用 ms-controller="VM1", 那么对于DIV里面的{{firstName}}就会解析成VM1的firstName中的值。

    有关它们的详细用法,可见这里

    模板绑定(ms-include)

    如果单是把DOM树作为一个模板远远不够的,比如有几个地方,需要重复利用一套HTML结构,这就要用到内部模板或外部模板了。

    内部模板是,这个模板与目标节点是位于同一个DOM树中。我们用一个MIME不明的script保存它,然后通过ms-include="id"引用它。

    <script type="text/avalon" id="tpl">
        here, {{ 1 + 1 }}
    </script>
     
    <div  ms-include="'tml'"></div>

    注意,ms-include的值要用引号括起,表示这只是一个字符串,这时它就会搜索页面的具有此ID的节点,取其innerHTML,放进ms-include所在的元素内部。否则这个tpl会被当成一个变量, 框架就会在VM中检测有没有此属性,有就取其值,重复上面的步骤。如果成功,页面会出现here, 2的字样。

    外部模板,通常用于多个页面的复用,因此需要整成一个独立的文件。这时我们就需要通过ms-include-src="src"进行加载。

    比如有一个HTML文件tmpl.html,它的内容为:

    <div>这是一个独立的页面</div>
    <div>它是通过AJAX的GET请求加载下来的</div>

    然后我们这样引入它

    <div  ms-include-src="'tmpl.html'"></div>

    数据填充(ms-text, ms-html)

    这分两种:文本绑定与HTML绑定,每种都有两个实现方式

    <script>
        
     avalon.define("test", function(vm) {
          vm.text = "<b> 1111  </b>"
     })
     
    </script>
    <div ms-controller="test">
        <div><em>用于测试是否被测除</em>xxxx{{text}}yyyy</div>
        <div><em>用于测试是否被测除</em>xxxx{{text|html}}yyyy</div>
        <div ms-text="text"><em>用于测试是否被测除</em>xxxx yyyy</div>
        <div ms-html="text"><em>用于测试是否被测除</em>xxxx yyyy</div>
    </div>

    类名切换(ms-class, ms-hover, ms-active)

    avalon提供了多种方式来绑定类名,有ms-class, ms-hover, ms-active, 具体可看这里

    事件绑定(ms-on)

    avalon通过ms-on-click或ms-click进行事件绑定,并在IE对事件对象进行修复,并统一了所有浏览器对return false的处理。具体可看这里

    avalon并没有像jQuery设计一个近九百行的事件系统,连事件回调的执行顺序都进行修复(IE6-8,attachEvent添加的回调在执行时并没有按先入先出的顺序执行),只是很薄的一层封装,因此性能很强。

    • ms-click
    • ms-dblclick
    • ms-mouseout
    • ms-mouseover
    • ms-mousemove
    • ms-mouseenter
    • ms-mouseleave
    • ms-mouseup
    • ms-mousedown
    • ms-keypress
    • ms-keyup
    • ms-keydown
    • ms-focus
    • ms-blur
    • ms-change
    • ms-on-*

    显示绑定(ms-visible)

    avalon通过ms-visible="bool"实现对某个元素显示隐藏控制,对于低版本的浏览器,它用的是style.display="none"进行隐藏,对于支持HTML5的浏览器,它是使用hidden属性来控制。因此它是优于其他MVVM的实现。

    插入绑定(ms-if)

    这个功能是抄自knockout的,ms-if="bool",同样隐藏,但它是将元素移出DOM。这个功能直接影响到CSS :empty伪类的渲染结果,因此比较有用。

    双工绑定(ms-duplex)

    这功能抄自angular,原名ms-model起不得太好,姑且认为利用VM中的某些属性对表单元素进行双向绑定。打算启用一个新名字叫ms-duplex

    这个绑定,它除了负责将VM中对应的值放到表单元素的value中,还对元素偷偷绑定一些事件,用于监听用户的输入从而自动刷新VM。具体如下:

    text, password, textarea
    默认是通过input事件进行监听,旧式IE是通过propertychange实现,换言之,每改一个字符串都触发。如果想在失去焦点时才触发,可以在元素上使用data-event="change"进行调整。 它要求VM对应的属性为一个字符串或数字,不过触发一次之后,属性就会变成字符串。
    radio
    默认是通过change事件进行监听,旧式IE是通过chick实现, 它要求VM对应的属性为一个布尔。
    checkbox
    默认是通过change事件进行监听, 它要求VM对应的属性为一个字符串数组。
    select
    默认是通过change事件进行监听, 它要求VM对应的属性为一个字符串或字符串数组(视multiple的值)。

    样式绑定(ms-css)

    用法为ms-css-name="value"

    <div ms-css-width="aaa"></div>

    数据绑定(ms-data)

    用法为ms-data-name="value", 用于为元素节点绑定HTML5 data-*属性。

    布尔属性绑定

    这主要涉及到表单元素几个非常重要的布尔属性,即disabed, readyOnly, selected , checked, 分别使用ms-disabled, ms-enabled, ms-readonly, ms-checked, ms-selected。ms-disabled与ms-enabled是对立的,一个true为添加属性,另一个true为移除属性。

    字符串属性绑定

    这主要涉及到几个非常常用的字符串属性,即href, src, alt, title, value, 分别使用ms-href, ms-src, ms-alt, ms-title, ms-value。它们的值的解析情况与其他绑定不一样,如果值没有{{}}插值表达式,那么就当成VM中的一个属性,并且可以与加号,减号混用, 组成表达式,如果里面有表达式,整个当成一个字符串。

    <a ms-href="aaa + '.html'">xxxx</a>
    <a ms-href="{{aaa}}.html">xxxx</a>

    万能属性绑定(ms-attr)

    ms-attr-name="value",这个允许我们在元素上绑定更多种类的属性,如className, tabIndex, name, colSpan什么的。

    万能绑定(ms-bind)

    ms-bind是一种非常强大的同步机制,因为它允许你持续监听某一个VM属性的变化,并且它的参数是一个函数,this又是指向绑定属性的元素节点,因此比ms-css, ms-attr, ms-data, ms-click等有着因定DOM操作的绑定来得更灵活。

    用法: ms-bind-prop="callback", 其中prop, callback都要求来自同一个VM。callback为一个函数,this指向元素节点。

    <div ms-controller="test">
         <div ms-bind-aaa="callback"></div>
         <button ms-click="one">点我</button>
    </div>
    avalon.define("test", function(vm) {
         vm.aaa = 1111;
         vm.callback = function() {
             this.innerHTML = vm.aaa
         }
         vm.one = function() {
             vm.aaa = new Date - 0
         }
     });

    循环绑定(ms-each)

    用法为ms-each-xxx="array", 其中xxx可以随意改,如yyy, el, 它是用于在子元素中进行引用。array对应VM中的一个普通数组或一个监控数组。详见这里

    <script>
        avalon.define("test", function(vm) {
            vm.array = [{value: "aaa", text: "111"}, {value: "bbb", text: "222"}, {value: "bbb", text: "333"}]
        })
    </script>
    <div ms-controller="test">
        <select ms-each-el="array">
            <option ms-value="el.value">{{$index}}、{{el}}</option>
        </select>
    </div>

    UI绑定(ms-ui)

    它的格式为ms-ui-$opts="uiName", 其他$opts可有可无,存在时对应VM中的一个对象,建议将它设置为不可监控的,因为它只是作为一个配置对象。uiName为控件的名字。

    此外,在绑定元素上还应该设置一个data-id属性,用于指定生成的UI控件对应的VM的名字。你也可以设置更多的data-*属性,方便用于配置UI。

    下面是一个完整的实例用于教导你如何定义使用一个UI。

    例子

    首先,以AMD规范定义一个模块,文件名为avalon.testui.js,把它放到与avalon.js同一目录下。内容为:

    define(["avalon"],function( av ) {
       //UI  控件的模板
      // 必须 在avalon.ui上注册一个函数,它有四个参数,最后一个是可选的,其他分别为容器元素,VM的ID名, vmodels
        av.ui["testui"] = function(element, id, vmodels, opts) {
            opts = opts || {}
            var model = av.define(id, function(vm) {
                vm.name = "这是控件的默认内容"
            })
            for (var i in opts) {
                if (model.hasOwnProperty(i)) {//必须要用hasProperty,因为model在IE6-8为一个VBS对象,不允许添加新属性
                    model[i] = opts[i]
                }
            }
            //必须在nextTick的回调里插入新节点 与 进行扫描
            av.nextTick(function() {
                element.innerHTML = "<div>{{ name }}</div>"
                //这里的格式是固定的
                av.scan(element, [model].concat(vmodels))
            })
            return model //这里必须返回VM对象,好让avalon.bindingHandlers.ui方法,将它放到avalon.vmodels中
        }
        return av //必须有返回值
    })

    然后页面这样使用它

    <script>
     
        require("avalon.testui", function() {
            avalon.define("test", function(vm) {
                vm.$opts = {
                    name: "这是控件的内容"
                }
            })
            avalon.scan()
            console.log(avalon.vmodels.ddd)
        })
     
     
    </script>
     
    <div ms-controller="test" ms-ui-$opts="testui" data-id="ddd"></div>

    $watch

    这是一个位于VM的方法,用于监听VM的某人属性的变化,回调中有两个传参,新属性值与旧属性值,里面的this指向VM,详见这里

    过滤器

    avalon从angular中抄来管道符风格的过滤器,但有点不一样。 它只能用于{{}}插值表达式。如果不存在参数,要求直接跟|filter,如果存在参传,则要用小括号括起,参数要有逗号,这与一般的函数调用差不多,如|truncate(20,"……")

    avalon自带以下几个过滤器

    html
    没有传参,用于将文本绑定转换为HTML绑定
    uppercase
    大写化
    lowercase
    小写化
    truncate
    对长字符串进行截短,truncate(number, truncation), number默认为30,truncation为“...”
    camelize
    驼峰化处理
    escape
    对类似于HTML格式的字符串进行转义,把尖括号转换为&gt; &lt;
    currency
    对数字添加货币符号,以及千位符, currency(symbol)
    number
    对数字进行各种格式化,这与与PHP的number_format完全兼容, number(decimals, dec_point, thousands_sep),
                 decimals	可选,规定多少个小数位。
                 dec_point	可选,规定用作小数点的字符串(默认为 . )。
                thousands_sep	可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。
                    
    date
    对日期进行格式化,date(formats)
    'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
    'yy': 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
    'y': 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
    'MMMM': Month in year (January-December)
    'MMM': Month in year (Jan-Dec)
    'MM': Month in year, padded (01-12)
    'M': Month in year (1-12)
    'dd': Day in month, padded (01-31)
    'd': Day in month (1-31)
    'EEEE': Day in Week,(Sunday-Saturday)
    'EEE': Day in Week, (Sun-Sat)
    'HH': Hour in day, padded (00-23)
    'H': Hour in day (0-23)
    'hh': Hour in am/pm, padded (01-12)
    'h': Hour in am/pm, (1-12)
    'mm': Minute in hour, padded (00-59)
    'm': Minute in hour (0-59)
    'ss': Second in minute, padded (00-59)
    's': Second in minute (0-59)
    'a': am/pm marker
    'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200)
    format string can also be one of the following predefined localizable formats:
     
    'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm)
    'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm)
    'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010)
    'longDate': equivalent to 'MMMM d, y' for en_US locale (e.g. September 3, 2010
    'mediumDate': equivalent to 'MMM d, y' for en_US locale (e.g. Sep 3, 2010)
    'shortDate': equivalent to 'M/d/yy' for en_US locale (e.g. 9/3/10)
    'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm)
    'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm)

    例子:

    生成于{{ new Date | date("yyyy MM dd:HH:mm:ss")}}

    生成于{{ "2011/07/08" | date("yyyy MM dd:HH:mm:ss")}}

    生成于{{ "2011-07-08" | date("yyyy MM dd:HH:mm:ss")}}

    生成于{{ "01-01-2000" | date("yyyy MM dd:HH:mm:ss")}}

    生成于{{ "03 04,2000" | date("yyyy MM dd:HH:mm:ss")}}

    生成于{{ "3 4,2000" | date("yyyy MM dd:HH:mm:ss")}}

    生成于{{ 1373021259229 | date("yyyy MM dd:HH:mm:ss")}}

    生成于{{ "1373021259229" | date("yyyy MM dd:HH:mm:ss")}}

    值得注意的是,new Date可传的格式类型非常多,但不是所有浏览器都支持这么多,详看这里

    多个过滤器一起工作

    <div>{{ prop | filter1 | filter2 | filter3(args, args2) | filter4(args)}}</div>

    如果想自定义过滤器,可以这样做

    avalon.filters.myfilter = function(str, args, args2){//str为管道符之前计算得到的结果,默认框架会帮你传入,此方法必须返回一个值
       /* 具体逻辑 */
       return ret;
    }

    AMD 加载器

    avalon装备了AMD模范的加载咕咕,这涉及到两个全局方法 require与define

    require(deps, callback)

    deps 必需。String|Array。依赖列表,可以是具体路径或模块标识,如果想用字符串表示多个模块,则请用“,”隔开它们。

    callback 必需。Function。回调,当用户指定的依赖以及这些依赖的依赖树都加载执行完毕后,才会安全执行它。

    模块标识

    一个模块标识就是一个字符串,通过它们来转换成到对应JS文件或CSS文件的路径。

    有关模块标识的CommonJS规范,可以见 这里

    具体约定如下:

    1. 每个模块标识的字符串组成只能是合法URL路径,因此只能是英文字母,数字,点号,斜扛,#号。
    2. 如果模块标识是 以"./"开头,则表示相对于它的父模块的目录中找。
    3. 如果模块标识是 以"../"开头,则表示相对于它的父模块的父目录中找。
    4. 如果模块标识不以点号或斜扛开始,则有以下三种情况 
      1. 如果此模块标识在 $.config.alias存在对应值,换言之某一模块定义了一个别名,则用此模块的具体路径加载文件。
      2. 如果此模块标识 以http://、https://、file:/// 等协议开头的绝对路径,直接用它加载文件。
      3. 否则我们将在引入框架种子模块(mass.js)的目录下寻找是否有同名JS文件,然后指向它。
    5. 对于JS模块,它可以省略后缀名,即“.js”可有可无;但对于CSS文件则不能省略。
    6. 框架种子模块的目录保存于 $.config.base属性中。
    7. ready是系统占位符,用于表示DOM树是否加载完毕,不会进行路径转换。
    8. 在种子模块内部已经默认提供了 所有核心模块的别名,以$开头加模块名,如$lang,$event。

    如果想禁止使用avalon自带的加载器,可以在第一次调用require方法之前,执行如下代码:

    avalon.config({loader: false})
    例子

    加载单个模块。

    // 由于lang.js与mass.js是位于同一目录下,可以省略./
    require("lang", function(lang) {
        alert(lang.String.toUpperCase("aa"))
    });
    例子

    加载多个模块。需要注意的是,涉及DOM操作时必须要待到DOM树建完才能进入,因此我们在这里指定了一个标识,叫"ready!", 它并不一个模块,用户自定义模块,也不要起名叫"ready!"。

    require("jquery,node,attr,ready!", function($) {
        alert($.fn.attr + "");
        alert($.fn.prop + "");
    });
    例子

    加载多个模块,使用字符串数组形式的依赖列表。

    require(["jquery", "css", "ready!"], function($, css) {
        $("#js_require_ex3").toggle();
    });
    例子

    加载CSS文件。

    require(["jquery", "ready!", "css!http//sdfds.xdfs.css"], function($) {
        $("#js_require_ex3").toggle();
    });
    例子

    使用别名机制管理模块的链接。

    var path = location.protocol + "//" + location.host + "/doc/scripts/loadtest/"
    require.config({
        alias: {
            "aaa": path + "aaa.js",
            "bbb": path + "bbb.js",
            "ccc": path + "ccc.js",
            "ddd": path + "ddd.js"
        }
    })
    require("aaa,bbb,ready", function(a, b, $) {
        var parent = $("#loadasync2")
        parent.append(a);
        parent.append(b);
        $("#asynctest2").click(function() {
            require("ccc,ddd", function(c, d) {
                parent.append(c);
                parent.append(d);
            })
        })
    });
    例子

    加载不按规范编写的JS文件,可以让你不用改jQuery的源码就加载它。相当于其他加载器的shim插件。 与别名机制不同的是,现在它对应一个对象,src为完整路径,deps为依赖列表,exports为其他模块引用它时,传送给它们的参数

    !function() {
                   var path = "http://files.cnblogs.com/shuicaituya/"
                   require.config({
                       alias: {
                           "jquery": {
                               "src": path + "jquery.js",
                               deps: [], //没有依赖可以不写
                               exports: "jQuery"
                           }
                       }
                   });
                   require("jquery", function($) {
                       alert($)
                       alert("回调调起成功");
                   })
               }()

    define方法用于定义一个模块,格式为:

    define( id?, deps?, factory )

    id
    可选。String。模块ID。它最终会转换一个URL,放于 $.modules中。
    deps
    可选。String|Array。依赖列表。
    factory
    必需。Function|Object。模块工厂。它的参数列参为其依赖模块所有返回的值,如果某个模块没有返回值,则对应位置为undefine
            
    例子

    加载不按规范编写的JS文件,可以让你不用改jQuery的源码就加载它。相当于其他加载器的shim插件。 与别名机制不同的是,现在它对应一个对象,src为完整路径,deps为依赖列表,exports为其他模块引用它时,传送给它们的参数

    //aaa.js 没有依赖不用改
    define("aaa", function() {
        return 1
    })
     
    //bbb.js  没有依赖不用改
    define("bbb", function() {
        return 2
    });
    //ccc.js
    define("ccc", ["$aaa"], function(a) {
        return 10 + a
    })
     
    //ddd/ddd.js
    define("ddd", ["$ddd"], function(c) {
        return c + 100
    });
     
     
     
    标签: avalon
  • 相关阅读:
    作业练习
    作业练习
    作业
    作业
    作业
    作业
    作业
    作业
    作业
    作业
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3181816.html
Copyright © 2020-2023  润新知