迷你MVVM框架 avalonjs1.5 入门教程

avalon通过几年之后,已成为国内一个举足轻重的框架。它提供了多种不一样的版本,知足不一样人群的须要。好比avalon.js支持IE6等老旧浏览器,让许多靠政府项目或对兼容性要求够高的公司也能享受MVVM的乐趣。avalon.modern.js支持IE10以上版本,优先使用新API,性能更优,体积更少。avalon.mobile.js在avalon.modern的基础提供了触屏事件的支持,知足你们在移动开发的需求。此外,它们分别存在avalon.xxx.shim版本,指无自带加载器版,avalon.xxx.min版本,指上线压缩版本。javascript

avalon早期严重受到angular与knockout的影响,API与它们很相近,通过多年的发展,渐渐摸索出本身一套模式。avalon1.5是一个里程碑的版本,它带来许多全新的特性,让咱们编写代码更加爽快。php

avalon1.5的下载地址: https://github.com/RubyLouvre/avalon/tree/1.5css

  1. 视图模型
  2. 非监控属性与监控属性
  3. 视图模型的做用域
  4. 扫描机制
  5. 指令(绑定)
  6. 数据填充(ms-text, ms-html)
  7. 模板绑定(ms-include)
  8. 类名切换(ms-class, ms-hover, ms-active)
  9. 事件绑定(ms-on,……)
  10. 显示绑定(ms-visible)
  11. 插入绑定(ms-if)
  12. 双工绑定(ms-duplex)
  13. 样式绑定(ms-css)
  14. 数据绑定(ms-data)
  15. 属性绑定(ms-attr)
  16. 循环绑定(ms-repeat,ms-each,ms-with)
  17. 动画绑定(ms-effect)
  18. 自定义标签组件
  19. 模块间通讯及属性监控 $watch,$fire
  20. 过滤器
  21. 自定义指令(绑定)
  22. 加载器
  23. AJAX
  24. 路由系统
  25. 在IE6下调试avalon
  26. 其余要注意的地方(更新VM等)

视图模型

avalon与jQuery最大的一个区别是,思惟的不一样。jQuery要操做一个元素,老是设法找到此元素,想象这个元素是否有ID,有某个类名,存在某个特定的标签下,是父节点的第几个孩子,诸如此类,最后拼凑出一个CSS表达式,而后$(expr)找到元素,而后再进行操做,因而JS代码里满屏$。维护代码的人,老是要对着页面来看看,这表达式是对应某某元素,若是只有ID,类名还好,新手非常写出很长的CSS表达式,致使你最后崩溃掉。html

avalon要操做某个元素,就直接在HTML为它添加一些指令,这些指令或者以ms-开头的元素属性,或是标签之间的4个花括号。指令里面存在某些变量,这些变量最后在JS汇集成一个对象,这就叫作VM( View Model, 视图模型 )。咱们只要操做这个VM的数据变更就好了,页面上就会自动变化。有了这一层的分离,咱们在代码量就少能许多操做DOM的代码,专致于业务自己。好比说:java

<p>{{aaa}}</p>

至关于jQuery的如下代码:node

$(function(){
   $("p").text(aaa) })

那咱们看看怎么定义一个VM吧。avalon在1.5以前存在两种定义方式,如今1.5只支持新风格,即git

var vm = avalon.define({
 $id: "test",  a: 1,  b: 2,  c: {  d: 1  },  onClick: function(e){  e.preventDefault()  },  arr: [1,2,3] }) 

avalon.define是一个很是重要的方法,要求传入一个对象,对象里面必须有$id属性,它是用于指定其在页面的做用范围。github

avalon.define会返回一个新对象,它除了以前咱们定的属性与方法,还添加了$watch, $events, $fire, $model等属性与方法。ajax

当咱们以vm.a = 4来从新赋值时,页面上用到a的地方会天然做出反应,这个行为称之为 绑定 ,有的属性会使用ms-duplex指令绑定到表单元素上,这时反应是双向的,input,select, textarea的值被用户改动时,会天然反应到VM上,而咱们对VM上的操做也会反应到表单元素上,这叫作 双工绑定json

有的东西,你压底只有它只做用一次,如大表格的数据展现,之后没有任何互动交互,那咱们有几种方式:

  • 将该属性的名字以$开头,以$aaa,这样标识它为非监控属性。
  • 将该属性的名字放到$skipArray数组,也能标识它非监控属性,由于有的名字须要先后一致,后端不肯意加$,这种方式更灵活。
  • 有的属性我想它在这里能够屡次改动,有的则显示就不改了,可使用 单向绑定,须要在ms-*属性的值前面加::,或花括号内部的前面加::
<!DOCTYPE html>
<html>  <head>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <script src="avalon.js"></script>   <script>    var vm = avalon.define({     $id: "test",     a: 1,     $b: 2,     $skipArray: ["a"],     c: 3    })    setTimeout(function () {     vm.a = 100     vm.$b = 100     vm.c = 100    }, 3000)   </script>  </head>  <body>   <div ms-controller="test">    <p>{{a}}不会变</p>    <p>{{$b}}不会变</p>    <p>{{::c}}不会变</p>    <p>{{c}}会变</p>   </div>  </body> </html> 

非监控属性与监控属性

VM是一个对象,它除了包含一些必须的方法与属性外,其余的东西就分为两大类,非监控属性与监控属性。

非监控属性,就是咱们上面指的以$开头,或是名字定义在$skipArray数组的东西,此外,当某属性的值的类型为函数或元素节点,文本节点,注释节点,文档对象,文档碎片与window时,它们也没法监控。

监控属性则分为4类:

  • 当其值为字符串,数值,布尔,undefined, null等简单数据类型时,为 监控属性
  • 当它定义在$computed对象中时,其值为一个对象,拥有get,set方法,为 计算属性 。计算属性老是有一个或一个以上的监控属性构成。
  • 当其值的类型为数组时,称之为 监控数组 ,咱们能够改动它的方法,来同步视图。在1.5中,咱们不断能够监听其长度变化,还能够监听其元素或元素的属性变化。
  • 当其值为一个对象时,它里面的对象也继续转换为计算属性,监控属性,这个对象咱们称之为 子VM

在1.5以前的版本,还有一个叫监控函数的东西,即里面包含了某些监控方法。但如今咱们不建议这样用,由于在将来的版本,咱们打算像angular那样经过纯静态词法分析,就能获得此指令所依赖的全部监控属性。而监控方法则须要使用动态的依赖检测实现。动态依赖检测虽然很是强大,但也很是耗性能。在1.5以前,avalon是彻底经过动态依赖检测实现绑定的,1.5是结合静态词法分析与动态依赖检测,将来会一点点改成纯静态词法分析。

var vm = avalon.define({
 $id: "test",  a: 1,  $b: 2,  $skipArray: ["a"],  c: 3, //监控属性  d: { //这是子VM   dd: {    ddd: 3   },   dd2: 4  },  arr: [1, 2, 3, 4], //监控数组  $computed: {   c: {//计算属性c    get: function () {     return this.a + " " + this.c    },    set: function (val) {     var arr = val.split(" ")     this.a = arr[0]     this.b = arr[1]    }   },   e: {//计算属性e    get: function () {     return this.a + 100    }   }  } }) 

视图模型的做用域

为了方便协做开发的需求,咱们引入了做用域的概念。由于一个页面可能很大,分为N个模块,每一个模块交同不一样的人来编写。这个在移动端的SPA应用中尤其明显。 对于JS,咱们能够拆分为N个JS文件,每一个JS文件都有本身的VM。页面也是拆成一块块,这能够经过PHP或nodejs的模板贴合起来。而在这以前,咱们先为它们加上ms-controller!

ms-controller为一个指令,其值为一个VM的$id,如ms-controller="test",它就会在avalon.vmodels中找到该VM,而后这个元素下方用到的全部指令中的变量,都应该位于此VM。

但若是一个功能模块特别复杂,它用到的字段特别多,意味着这个VM也要定义许多许多属性,而这些属性的某一部分也在其余页面或模块用到,这时咱们就须要对它进行拆分,方便重用。拆分后的两个对象或N个对象,avalon容许咱们以ms-controller套ms-controller的形式,实现做用域间的数据共享。换言之,若是某变量在当前的VM换不到,它就会往上找,在上面的VM中查找此属性,一直找到为止。这有点像JS的对象属性查找,其实,它像CSS的做用域查找,由于咱们还引入了ms-important。ms-important的寓意就是CSS中的important!符号,就在此做用域查找,不往外找!

此外,还有些地方,你不想avalon来处理它们,如script标签的内容,style标签的内容,文章的语法高亮部分,引用别人文章的部分,这个可使用ms-skip指令来绕开这些无用的区域。

至少,咱们学习了ms-controller, ms-important, ms-skip, 更详细能够到 新官网 上学习

扫描机制

avalon能实现VM与视图之间的互动,最关键的东西就是这个。在有的MVVM框架,这也叫作编译(compile),意即,将视图的某一部分的全部指令所有抽取出来,转换为一个个视图刷新函数,而后放到一个个数组中,当VM的属性变更时,就会执行这些数组的函数。固然数组里面的东西不定是函数,也多是对象,但里面确定有个视图刷新函数。这是MVVM框架的核心机制,但怎么抽取出来,每一个框架的方式都不同。avalon将这个过程称之为扫描。扫描老是从某个节点开始。在avalon内部,已经默认进行了一次扫描,从body元素开始描。若是咱们为页面插入了什么新内容,而这个区域里面又包括了avalon指令,那么咱们就须要手动扫描了。

avalon.scan是avalon第二重要的API,它有两个参数,第一个是元素节点,第二个是数组,里面为一个个VM。固然这两个参数是可选的。但当你手动扫描时,最好都会进去,这样会加快扫描速度,并减小意外。由于全部指令,都扫描后就变移除掉,这包括指定VM用的ms-controller,ms-important!

<!DOCTYPE html>
<html>  <head>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <script src="avalon.js"></script>   <script>    avalon.ready(function () {     var div = document.createElement("div")     div.innerHTML = "{{aaa}}"     div.setAttribute("ms-controller", "eee")     document.body.appendChild(div)     var vm = avalon.define({      $id: "eee",      aaa: 111     })     avalon.scan(div, vm)    })   </script>  </head>  <body>  </body> </html> 

指令(绑定)

指令是指写在HTML中的特殊符号,包括如下几种,ms-开头的绑定属性,写在innerText里面的{{}}的插值表达式,相似data-duplex-xxx的辅助指令(data-后面跟着的绑定属性的名字,它们必须与绑定元素定义在同一元素),还有新添加的自定义标签(它们必须带有:号)

新手们或从angular过来的人很容易犯一个错误,就是直接在属性值里面加一个{{}},觉得就能绑定了,却不知avalon为了性能优化,会跳过全部非ms-*属性。

这里拥有 全部指令的一览图

数据填充

这里提供ms-text, ms-html两种指令,其余ms-text拥有{{expr}}这个变体,ms-html拥有{{expr|html}}这个变体。当大家页面也使用后端模板拼凑而成时,可能 后端会占用了{{}}界定符,咱们能够经过如下配置方式从新指定界定符

avalon.config({ interpolate:["{%","%}"] })

而且咱们能够经过avalon.config.openTag, avalon.config.closeTag获得“{%”,"%}"。注意,界定符里面千万别出现<, >,由于这存在 兼容性问题 。这两个界定符也不能同样,最好它们的长度都大于1。

<script> avalon.define({  $id: "test",  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-attr、ms-css、ms-include、ms-class、 ms-href、 ms-title、ms-src等。一旦出现插值表达式,说明这个整个东西分红可变的部分与不可变的部分,{{}}内为可变的,反之亦然。 若是没有{{}}说明整个东西都要求值,又如ms-include="'id'",要用两种引号强制让它的内部不是一个变量。

模板绑定

ms-include指令是ms-html的有效补充。咱们知道ms-html是将VM中某个符合HTML结构的字符串,放到某元素底下解析为节点。但若是这个字符串很大,放在VM上就不合算,这时咱们就想到将它到页面的某个位置上(如script, noscript, textarea等能放大片内容的特殊标签)或干脆独立成一个HTML文件。因而前者叫作内部模板,由于是放在页面的内部,后者叫作外部模板。对于前者,咱们使用ms-include=“expr”来引用,后者,咱们是使用ms-include-src="expr"来引用。src表示一个路径,所以其值每每是一个URL地址,为了你们方便拼接URL,咱们容许ms-include-src的值可使用插值表达式。如ms-include-src="aaa/{{bbb}}.html"。因为咱们加载外部模板时是用AJAX实现的,所以你们在调试代码时,必须打开WEB服务器。

<html>  <head>   <meta http-equiv="content-type" content="text/html; charset=UTF-8" />   <script src="avalon.js"></script>   <script>    avalon.define({     $id: "test",     xxx: "引入内部模板"    })   </script>  </head>  <body >   <script type="avalon" id="tpl">    here, {{ 3 + 6 * 5 }}   </script>   <div ms-controller="test">    <p>{{xxx}}</p>    <div ms-include="'tpl'"></div>   </div>  </body> </html> 

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

若是你们想在模板加载后,加工一下模板,可使用data-include-loaded来指定回调的名字。

若是你们想在模板扫描后,隐藏loading什么的,可使用data-include-rendered来指定回调的名字。

因为ms-include绑定须要定义在一个元素节点上,它的做用仅仅是一个占位符,提供一个插入位置的容器。 若是用户想在插入内容后,去掉这容器,可使用data-include-replace="true"。

avalon在使用ms-include-src 加载外部模板时,会将它们存放到avalon.templateCache对象中,所以咱们能够搞出一种架构出来,在上线前,将全部要远程加载的模板所有打包到avalon.templateCache对象中,这样它在发出请求前,先查找此对象,发现存在就不会发出请求了。

注意,不管是ms-include仍是ms-include-src都会在其值变化时,请空原元素的全部子孙节点,致使原有数据丢失,里面用到的全部组件从新生成,若是保持原来的节点,可使用data-include-cache="true"辅助指令。

下面是一个经典的后台系统框架!

<!DOCTYPE html>
<html>  <head>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <script src="avalon.js"></script>   <script>    avalon.templateCache = {     aaa: "<div>这里是很是复杂的HTML结构1</div>",     bbb: "<div>这里是很是复杂的HTML结构2</div>"     ccc: "<div>这里是很是复杂的HTML结构3</div>"     ddd: "<div>这里是很是复杂的HTML结构4</div>"    }    var vm =avalon.define({     $id:"root",     tabs:["aaa","bbb", "ccc","ddd"],//全部标签页的名字     curTab: "aaa",     switchTab: function(el){      vm.curTab = el     },     showLoading: function(){},     hideLoading:function(){}    })   </script>  </head>  <body ms-controller="root">   <table>    <tr>     <td>      <ul>       <li ms-repeat="tabs" ms-click="switchTab(el)">{{el}}</li>      </ul>     </td>     <td>      <!--主内容显示区-->      <div ms-include-src="curTab"       data-include-loaded="showLoading"       data-include-rendered="hideLoading"       >      </div>     </td>    </tr>   </table>  </body> </html> 

更详细的内容可见 新官网

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

avalon1.5如今只支持新风格,即ms-class="aaa: true"这种形式,此绑定属性的值以冒号分为两部分,前面为类名,后面表示添加或移除。 ms-class="aaa bbb ccc: toggle",当toggle在VM中为true时,它会为元素同时添加aaa, bbb, ccc三个类名。冒号及其后面的东西也不是必须的, 如ms-class="aaa1 bbb2",表示老是为元素添加aaa1,bbb2这两个类名。前面的部分也可使用插值表达式动态生成,如ms-class="{{className}}:true", className在VM是什么,就会为元素添加什么类名。若是你想为元素添加多个类名绑定,可使用ms-class-1="aaa: true" ms-class-2="bbb:toggle"来添加。

ms-hover, ms-active与ms-class的用法相同,但它们一个只在鼠标掠过元素上方时添加类名,移走时移除;另外一个则在元素得到焦点时(好比点击)添加类名,失去焦点时移除。

更详细的内容可见 新官网

事件绑定

咱们能够经过ms-on-*为元素绑定各类事件,好比ms-on-click=fn,表示为当前元素绑定点击事件,fn为VM的一个函数。默认地,咱们会为fn传入一个参数event,咱们已经为它作了兼容处理,所以你在IE下也能使用preventDefault, stopPropagation, pageX, pageY, target, timeStamp, which等标准属性与方法。若是你还想传其余参数,还想用事件对象,能够用$event占位。ms-on-click=fn(aaa, bbb, $events)。此外,咱们为全部经常使用事件作了快捷处理,所以大家还能够这样用,ms-click=fn2, ms-mouseover=fn3, ms-mouseleave=fn4。 注意,事件绑定的值的第一个单词必须是VM中的函数名字,你不能在其值里面使用加减乘除,如 ms-click="aaa+bbb",这样是不对的。若是 你想同时绑定多个点击事件,用法与ms-class,ms-hover同样,在后面加数字就好了。ms-click-1=fn1 ms-click-2=fn2 ms-click-3=fn3。

<!DOCTYPE HTML>
<html>  <head>   <title>ms-on</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({     $id: "test",     firstName: "司徒",     array: ["aaa", "bbb", "ccc"],     argsClick: function(e, a, b) {      alert([].slice.call(arguments).join(" "))     },     loopClick: function(a, e) {      alert(a + " " + e.type)     },     status: "",     callback: function(e) {      model.status = e.type     },     field: "",     check: function(e) {      model.field = this.value + " " + e.type     },     submit: function() {      var data = model.$model      if (window.JSON) {       setTimeout(function() {        alert(JSON.stringify(data))       })      }     }   })   </script>  </head>  <body>   <h3 style="text-align: center">ms-on-*</h3>   <fieldset ms-controller="test">    <legend>有关事件回调传参</legend>    <div ms-mouseenter="callback" ms-mouseleave="callback">{{status}}<br/>    <input ms-on-input="check"/>{{field}}    </div>   <div ms-click="argsClick($event, 100, firstName)">点我</div>    <div ms-each-el="array" >     <p ms-click="loopClick(el, $event)">{{el}}</p>    </div>    <button ms-click="submit">点我</button>   </fieldset>  </body> </html> 
<!DOCTYPE HTML>
<html>  <head>   <title>ms-on</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 count = 0    var vm = avalon.define({     $id: "multi-click",     str1: "1",     str2: "2",     str3: "3",     click0: function () {      vm.str1 = "xxxxxxxxx" + (count++)     },     click1: function () {      vm.str2 = "xxxxxxxxx" + (count++)     },     click2: function () {      vm.str3 = "xxxxxxxxx" + (count++)     }    })   </script>  </head>  <body>   <fieldset>    <legend>一个元素绑定多个同种事件的回调</legend>    <div ms-controller="multi-click">     <div ms-click="click0" ms-click-1="click1" ms-click-2="click2" >请点我</div>     <div>{{str1}}</div>     <div>{{str2}}</div>     <div>{{str3}}</div>    </div>   </fieldset>  </body> </html> 

更详细的内容可见 新官网

显示绑定(ms-visible)

ms-visible="expr"是经过改变元素的style.display值来控制元素的显示隐藏。

在1.5中结合动画指令还可使用动画效果。

<!DOCTYPE HTML>
<html>  <head>   <title>ms-visible</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>    avalon.define({     $id: "test",     a: true    })   </script>   <style>    .rect{     width:100px;     height:100px;     background: red;     display: none;     border:1px solid red;     text-align: center    }   </style>  </head>  <body>   <h3>ms-visible</h3>   <div ms-controller="test">    <p>点我隐藏或显示下面的方块<input ms-duplex-checked="a" type="radio"></p>    <div class="rect" ms-visible="a" >visible</div>   </div>  </body> </html> 

更详细的内容可见 新官网

插入绑定(ms-if)

ms-if 与 ms-visible的效果很像,都是不显示元素,但ms-if更进一步,将元素移出原来的位置藏到别处。而且它还会延迟其内容元素的扫描与绑定,所以相对于ms-visible性能更好。若是你想ms-if 与ms-repeat用在同一元素,让某个不合格的数组元素表明的那个地方不显示出来,那你请务必使用ms-if-loop!

在1.5中结合动画指令还可使用动画效果。

<!DOCTYPE HTML>
<html>  <head>   <title>ms-if</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>   <style>    .active{     background: blueviolet;    }   </style>   <script>    var vm = avalon.define({     $id: "test",     data: {      toggle: true     },     toggle: function () {      vm.data.toggle = !vm.data.toggle     }    });   </script>  </head>  <body>   <div class="avalonHide">测试avalonHide</div>   <div ms-controller="test">    <div ms-if="data.toggle" class="active">能动态添加移除</div>{{data.toggle}}    <p><button type='button' ms-click='toggle'>点我</button></p>   </div>  </body> </html> 

更详细的内容可见 新官网

双工绑定(ms-duplex)

MVVM框架中最重要的功能之一,惟一一个能让视图同步VM的指令。值得注意的是,它绑定的元素只是 表单元素 ,input, textarea, select。

在avalon1.5中,ms-duplex只支持duplex2.0。所谓的duplex2.0是指, 咱们能够从表单元素的checked属性,value属性抽取数据,转变成所须要的字符串类型,数值类型,布尔类型或数组类型,或添加更多拦截器进行更多操做。

ms-duplex实际上是默认使用了string 拦截器,即至关于ms-duplex-string。

其余内置拦截器分别是ms-duplex-number, ms-duplex-boolean, ms-duplex-checked

名字 应用元素 效果
ms-duplex-checked radio, checkbox 将其checked值同步到VM
ms-duplex-string 全部表单元素 将其value值同步到VM,对于checkbox, select[multiple=true]会进一步转换为数组
ms-duplex-number 全部表单元素 将其value值同步到VM, 
对于checkbox, select[multiple=true]会进一步转换为数组; 
咱们还能够经过data-duplex-number='strong|medium|weak'辅助指令来转换非数值 
为strong时,不能转的都转换为0; 
为medium时,空字符串会忽略转换,不然转换0或其余数字; 
为weak时,不是数值形式的值不作转换
ms-duplex-boolean 全部表单元素 将其value值同步到VM, 
对于checkbox, select[multiple=true]会进一步转换为数组; 
转换方式为,当其值为“true”转换为true,其余都转换为false

默认地,对于文本域,文本区,密码域,框架是使用oninput事件进笨监听,即用户每改动一个字符都会同步到VM。若是想在失去焦点时同步VM,可使用 data-duplex-event="change"辅助指令来调整。

另,咱们还能够为元素添加一个辅助指令, data-duplex-changed="fn", fn为VM中的一个函数。当元素的值 改动时,它会为回调传入当前的值及回调内部的this指向当前表单元素。

咱们还能够在avalon.duplexHooks上添加本身的拦截器。

<!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({     $id: "test",     text: "xxx",     textcb: "",     textchange: function(a) {      model.textcb = a     },     radio: "radio",     radiocb: "",     radiochange: function(a) {      model.radiocb = a     },     select: "2222",     selectcb: "",     selectchange: function(a) {      model.selectcb = a     }    })   </script>  </head>  <body ms-controller="test">   <h3>data-duplex-changed回调</h3>   <div><input ms-duplex="text" data-duplex-changed="textchange">{{textcb}}</div>   <div><input ms-duplex-checked="radio" type="radio" data-duplex-changed="radiochange">{{radiocb}}</div>   <div>    <select ms-duplex="select" data-duplex-changed="selectchange">     <option>1111</option>     <option>2222</option>     <option>3333</option>     <option>4444</option>    </select>    {{selectcb}}</div>  </body> </html> 

咱们能够经过如下方式定义一个拦截器,在avalon.duplexHooks上添加一个对象,此对象拥有init, get, set这三个方法,此三个方法不必定要写全,只要某一个就好了。init在初始化时调用,get方法是同步到VM时调用,set方法是同步视图时调用,这些方法都有两个参数,第一个参数是当前值,第二个参数是绑定对象。

有了拦截器,咱们就能够实现各类表单验证,而且一个ms-duplex是能够带N多拦截器的,如ms-duplex-number-notempty-gt10

<!DOCTYPE html>
<html>  >head>   <title>TODO supply a title</title>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width">   <script src="../avalon.js" ></script>   <script>    var model = avalon.define({     $id: "test",     a: 111    });    avalon.duplexHooks.add100 = {     get: function (val) {      return val     },     set: function (val) {      return val + 100     }    }   </script>  </head>  <body>   <div ms-controller="test">    <input ms-duplex-number-add100="a" >{{a}}   </div>  </body> </html> 

avalon的 oniui的验证组件 也是基于拦截器机制构建的,你们能够参考一下,方便本身设计本身的表单验证。

更详细的内容可见 新官网

样式绑定(ms-css)

用法为ms-css-name="value"。其值可使用插值表达式, 如ms-css-width=”prop”(会自动补px),ms-css-height=”{{prop}}%”, ms-css-color=”prop”, ms-css-background-color=”prop”, ms-css-background-image=”url({{imageUrl}})”, ms-css-font-size=”{{prop}}px”

注意:属性值不能加入CSS hack与important!

<!DOCTYPE html>
<html>  <head>   <title>by 司徒正美</title>   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">   <script src="../avalon.js"></script>   <script>    avalon.define({    $id: "test",    o: 0.5,    bg: "#F3F"// 不能使用CSS hack,如 bg : "#F3F\9\0"    })   </script>   <style>    .outer{     width:200px;     height: 200px;     position: absolute;     top:1px;     left:1px;     background: red;     z-index:1;    }    .inner{     width:100px;     height: 100px;     position: relative;     top:20px;     left:20px;     background: green;    }   </style>  </head>  <body ms-controller="test" >   <h3>在旧式IE下,若是父元素是定位元素,但没有设置它的top, left, z-index,那么为它设置透明时,    它的全部被定位的后代都没有透明</h3>   <div class="outer" ms-css-opacity="o" ms-css-background-color="bg" >    <div class="inner"></div>   </div>  </body> </html> 

更详细的内容可见 新官网

数据绑定(ms-data)

为当前元素添加data-*属性。能够参见ms-attr的用法。

更详细的内容可见 新官网

属性绑定(ms-attr)

其用法为ms-attr-name="value", value可使用插值表达式。当value为undefined, null, false,会移除此属性,不然会添加此属性。

<!DOCTYPE html>
<html>  <head>   <title>ms-attr-*</title>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width">   <script src="../avalon.js"></script>   <script>    var vm = avalon.define({     $id: "test",     aaa: true,     bbb: "@@@",     ccc: "&&&",     active: "active",     click: function(){      vm.aaa = !vm.aaa     }    })   </script>   <style>    .active {     background: goldenrod;    }    .readonly{     border:1px solid blueviolet;    }   </style>  </head>  <body>   <form method="get" action="aaa.html" ms-controller="test">    <input ms-enabled="aaa" name="a1" value="12345"/>    <input ms-disabled="aaa" name="a2" value="67890"/>    <input ms-readonly="aaa" name="a3" ms-class="readonly: aaa" value="readonly" />    <input ms-duplex-radio="aaa" type="checkbox" value="checkbox" name="a4"/>    <select name="a5">     <option>222</option>     <option ms-selected="aaa">555</option>    </select>    <p>     <input ms-attr-value="其余内容 {{ccc}}" name="a6" value="改"/>     <input ms-attr-value="'其余内容 '+ccc" name="a7" value="改" />     <input ms-value="其余内容 {{ccc}}" name="a8" value="改"/>    </p>    <button type="button" ms-click="click" ms-attr-class="active">     点我    </button>    <input type="submit" value="提交" />    <svg width="100" height="100">    <circle ms-attr-cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />    </svg>   </form>  </body> </html> 

更详细的内容可见 新官网

循环绑定(ms-repeat, ms-each, ms-with)

这三个东西都很相像,见下表

名字 循环的类型 循环的范围
ms-repeat 用于循环数组与对象 循环当前元素
ms-each 用于循环数组 循环当前元素的内部
ms-with 用于循环对象 循环当前元素的内部

当咱们用ms-each, ms-repeat循环数组时,能够为元素指定其别名,如ms-each-item="array1", ms-repeat-elem="array2", 若是你不指定别名,则默认为el。(注意,别名不能出现大写,由于属性名在HTML规范中,会所有转换为小写,详见 这里 )。监控数组拥有原生数组的全部方法,而且比它还多了set, remove, removeAt, removeAll, ensure, pushArray与 clear方法 。详见 这里 。

注意,ms-each, ms-repeat会生成一个 新的代理VM对象 放进当前的vmodels的前面,这个代理对象拥有el, $index, $first, $last, $remove, $outer等属性。另外一个会产生VM对象的绑定是ms-widget。

  1. el: 不必定叫这个名字,好比说ms-each-item,它就变成item了。默认为el。指向当前元素。
  2. $first: 断定是否为监控数组的第一个元素
  3. $last: 断定是否为监控数组的最后一个元素
  4. $index: 获得当前元素的索引值
  5. $outer: 获得外围循环的那个元素。
  6. $remove:这是一个方法,用于移除此元素

咱们还能够经过data-repeat-rendered, data-each-rendered来指定这些元素都插入DOM被渲染了后执行的回调,this指向元素节点, 有一个参数表示为当前的操做,是add, del, move, index仍是clear

若是循环的是对象,那么代理VM就有$val, $key, $index, $outer等属性。$key, $val分别引用键名,键值。另咱们能够经过指定data-with-sorted回调,规定只输出某一部分建值及它们的顺序。 注意,此绑定已经不建议使用,它将被ms-repeat代替,ms-repeat里面也可使用data-with-sorted回调。

<!DOCTYPE html>
<html>  <head>   <title></title>   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">   <script src="avalon.js"></script>   <style>    .id2013716 {     width: 200px;     float:left;    }   </style>   <script>    var a = avalon.define({    $id: "array",     array: ["1", "2", "3", "4"]    })    setTimeout(function() {     a.array.set(0, 7)    }, 1000);    var b = avalon.define({     $id: "complex",     array: [{name: "xxx", sex: "aaa", c: {number: 2}}, {name: "yyy", sex: "bbb", c: {number: 4}}]//    });    setTimeout(function() {     b.array[0].c.number = 9     b.array[0].name = "1000"    }, 1000)    setTimeout(function() {     a.array.push(5, 6, 7, 8, 9)    }, 1000)    setTimeout(function() {     a.array.unshift("a", "b", "c", "d")    }, 2000)    setTimeout(function() {     a.array.shift()     b.array[1].name = 7    }, 3000)    setTimeout(function() {     a.array.pop()    }, 4000)    setTimeout(function() {     a.array.splice(1, 3, "x", "y", "z")     b.array[1].name = "5000"    }, 5000)   </script>  </head>  <body>   <fieldset class="id2013716" ms-controller="array">    <legend>例子</legend>    <ul ms-each="array">     <li >数组的第{{$index+1}}个元素为{{el}}</li>    </ul>    <p>size: <b style="color:red">{{array.size()}}</b></p>   </fieldset>   <fieldset class="id2013716" ms-controller="complex">    <legend>例子</legend>    <ul >     <li ms-repeat-el="array">{{el.name+" "+el.sex}}它的内容为 number:{{el.c.number}}</li>    </ul>   </fieldset>  </body> </html> 

更详细的内容可见 新官网

动画绑定(ms-effect)

avalon1.5新增的功能,容许咱们用avalon.effect方法定义一个动画,而后在页面上存在ms-if,ms-repeat,ms-include,ms-visible的元素添加ms-effect="effectName"实现动画效果。此动画可使用CSS3与JS实现。

更详细的内容可见 新官网

自定义标签组件

经过自定义标签声明使用某个组件,这是avalon1.5引入的最重要特性。

早在20年前,JAVA也想经过标签库实现堆积木般的开发。但鉴于当时JAVAer的技术力量,这东西没有作成;如今谷歌有了本身的浏览器,实现了一套WEB Components规范,开源polymer,也是往这条路子走下去。由于以标签的形式声明组件比起在JS里以类的形式建立组件,来得更简单明了。

<oni:buttonset> <oni:button color="danger" type="icon" icon-position="left" icon="\&\#xf04c;"></oni:button> <oni:button color="danger" type="icon" icon-position="left" icon="\&\#xf04b;"></oni:button> <oni:button color="danger" type="icon" icon-position="left" icon="\&\#xf074;"></oni:button> </oni:buttonset>

作这套东西有几个难点:

  • 生命周期管理,从构建配置对象,到模板微调,到初始化,到子组件就绪,到自身组件就绪,到组件销微,都有本身相应的回调。
  • 继承机制
  • 当前组件必须等到子组件就绪才闭合自身,好比父组件的高度是由子组件决定
  • 组件的某些很大块的区块如何定义(这个引入插入点的概念处理),如弹出层有title, content, footer

为了让一个页面使用多个UI库,avalon引入了命名空间的概念,这是来自早期XML的设计。一个标签名由冒号隔开,前面是命名空间,后面是标签名。

avalon.library("oni", {})//这是声明一个命名空间,也是算声明一个UI库

在定义一个UI组件,最少有3个文件,JS文件(引用其余两个文件或更多子组件的JS),HTML文件(组件的模块),CSS文件(它能够由SASS更高级的语言生成)。

define(["avalon",
 "text!./avalon.pager.html",  "css!../chameleon/oniui-common.css",  "css!./avalon.pager.css" ], function (avalon, template) {  var _interface = function () {  }  avalon.component("oni:pager", {   regional: {}, //@config {Object} 默认语言包   perPages: 10, //@config {Number} 每页包含多少条目   showPages: 10, //@config {Number} 中间部分一共要显示多少页(若是两边出现省略号,即它们之间的页数)   currentPage: 1, //@config {Number} 当前选中的页面 (按照人们平常习惯,是从1开始),它会被高亮   _currentPage: 1, //@config {Number} 跳转台中的输入框显示的数字,它默认与currentPage一致   totalItems: 200, //@config {Number} 总条目数   totalPages: 0, //@config {Number} 总页数,经过Math.ceil(vm.totalItems / vm.perPages)求得   pages: [], //@config {Array} 要显示的页面组成的数字数组,如[1,2,3,4,5,6,7]   ellipseText: "…", //@config {String} 省略的页数用什么文字表示   prevText: "<", //@config {String} “下一页”分页按钮上显示的文字   nextText: ">", //@config {String} “上一页”分页按钮上显示的文字   firstPage: 0, //@config {Number} 当前可显示的最小页码,不能小于1   $ready: function(){    //....  }  //.... }) }) 

avalon.component是用来定义一个组件,第一个参数为自定义标签的名字(包括命名空间),第二个是配置对象,里面定义这个组件用到的属性,状态与方法。

更详细的内容可见 新官网

咱们也能够到 这里 直接看pager, button, checkboxlist是怎么定义。

将来与avalon配套使用的 OniUI 一点点所有改为自定义标签的形式,更但愿你们一块儿为avalon设计更多的UI组件。

模块间通讯及属性监控 $watch,$fire

avalon1.5是无比强大,并如今只有VM第一层有$watch, $fire方法,并去掉$unwatch方法。$watch方法会返回一个方法,调用它就会去掉此回调。

早期$watch只能监听当前层的属性,不能监听其子对象的属性,尤为在数组中,没法监听元素的属性变更。所以在avalon1.5中,引入通配符及多级监听功能。

var vm = avalon.define({
 $id: "test",  array: [1, 2, 3],  arr: [{a: 1}, {a: 2}, {a: 3}],  obj: {   a: 1,   b: 2  },  a: {   b: {    c: {     d: 33    }   }  } }) vm.$watch("array.*", function (a, b) {  expect(a).to.be(6)  expect(b).to.be(2) }) vm.$watch("arr.*.a", function (a, b) {  expect(a).to.be(99)  expect(b).to.be(1) }) vm.$watch("obj.*", function (a, b, c) {  expect(a).to.be(111)  expect(b).to.be(1) }) vm.$watch("a.*.c.d", function (a, b, c) {  expect(a).to.be(88)  expect(b).to.be(33) }) setTimeout(function () {  vm.array.set(1, 6)  vm.arr[0].a = 99  vm.obj.a = 111  vm.a.b.c.d = 88 }, 100) 

注意,*通配任只能出现一次

var unwatch = vm.$watch("array.*", function (a, b) { expect(a).to.be(6) expect(b).to.be(2) }) unwatch() //移除当前$watch回调

若是在跨模块通讯,因为全部VM都储存在avalon.vmodels中,咱们能够遍历它们,找到目标VM,而后$fire。

过滤器

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

avalon自带如下几个过滤器

html
没有传参,用于将文本绑定转换为HTML绑定
sanitize
去掉onclick, javascript:alert等可能引发注入攻击的代码。
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; }

自定义指令(绑定)

avalon1.5新添加的API,avalon.directive, 用于快速建立一种全新的指令!

过去avalon自定义绑定比较麻烦,很是影响avalon推广,也不利于业务上各类功能的开发,新的定义方式将变得很是简单

avalon.directive(name, {
  init: function(binding){
 //在这里处理binding.expr属性 }, update: function(value, oldValue){ //value是binding.expr通过解析获得的当前VM属性的值 //oldValue是它以前的值 }, }) 

拿最简单的data绑定来讲吧:

avalon.directive("data", {
 priority: 100,  update: function (val) {   var elem = this.element   var key = "data-" + this.param   if (val && typeof val === "object") {    elem[key] = val   } else {    elem.setAttribute(key, String(val))   }  } }) 

咱们发现它没有init回调,init主要是对binding.expr进行加工,或绑定事件的,它这些都不须要, 就会直接返回原始值.好比说,<div ms-data-xxx="yyy"< 这里的属性会抽取成下面一个对象:

var binding = {
 name: "ms-data-xxx",  expr: "yyy",  type: name,  element: DIVElement,  param: "xxx",  oneTime: false,  uuid: //框架会在这里生成一个UUID给它  priority://框架会根据上面的定义,计算出来,大概是1000+ ; } 

假如此时vm.yyy = 999; 那么第一次update时,value与oldValue分别为 999, undefined

对于用户来讲,priority通常不须要设置, 其计算公式为

//详见 scanAttr
priority: (directives[type].priority || type.charCodeAt(0) * 10) + (Number(param.replace(/\D/g, "")) || 0)

此外,若是你的绑定须要绑定一些事件来监听用户行为,那么你得在init或update添加一个roolback回调,在里面解绑定事件.

update方法中的this就是init的传参binding

那么咱们作一下简单的例子吧:

<!DOCTYPE html>
<html>  <head>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <script src="avalon.js">   </script>   <script>    avalon.directive("foo", {     init: function (binding) {      var elem = binding.element      var vmodels = binding.vmodels      var remove = avalon(elem).bind("click", function () {       elem.innerHTML = new Date - 0       for (var i = 0, v; v = vmodels[i++]; ) {        if (v.hasOwnProperty(binding.expr)) {         v[binding.expr] = elem.innerHTML         break        }       }      })      binding.roolback = function () {       avalon(elem).unbind("click", remove)      }     },     update: function (value, oldValue) {      this.element.innerHTML = value     }    })    var vm = avalon.define({     $id: "test",     aaa: 111    })    vm.$watch("aaa", function (a, b) {     console.log(a, b)    })   </script>  </head>  <body>   <div ms-controller="test">    <div ms-foo="aaa">点我</div>   </div>  </body> </html> 

别看例子简单,其实它是除duplex外又一个新的 双工绑定 !

加载器

avalon自带加载器,它是使用业界最通用的AMD规范。

若是你想禁用自带加载器,有两种办法:

  • 先入avalon再引入其余加载器,而后当即禁用avalon自带加载器
    avalon.config({ loader:false })
  • 到 这里 下载没有加载器的版本(以shim结尾)

至于avalon是怎么与jQuery, requirejs怎么搭配,怎么打包,能够详看 这里 。

AJAX

avalon自己没提供AJAX模块,你们可使用jQuery或 mmRequest

require(["mmRequest/mmRequest"], function() {  var vm = avalon.define("test", function(vm) {   vm.loadScript = function() {    avalon.getScript("test.js", function() {     console.log("success")     vm.time = + new Date()    })   }   vm.loadAJAX = function() {    avalon.ajax({     url: "test.js",     dataType: "script",     data: {      page: 1,      name: 1     },     success: function() {      console.log("success")      vm.time = + new Date()     }    })   }   vm.jsonData = []   vm.time = ""  })  avalon.scan() }) 

mmRequest覆盖原jQuery全部的功能,其文档地址在 这里 。

若是咱们想将VM提交到后台,因为VM存在许多方法与其余东西,直接提交数据量很大,所以咱们能够经过下面方法提交纯数据

$("button").keyup(function(){ $.post("demo_ajax_gethint.asp", JSON.parse(JSON.stringify(vm.$model)),function(result){ alert("提交成功") }); });

路由系统

avalon提供了mmRouer与mmState两个路由器,但mmState也是基于mmRouter开发的,引入mmState就会自动引入mmRouter。

这里两个加载器均可以在 这里 下载到,包括(mmPromise, mmHIstory, mmRouter, mmState)

其文档放在 这里 ,有不懂的地方直接在此仓库提ISSUE

在IE6下调试avalon

因为IE6下没有console.log,若是又不想用VS等巨无霸IDE,能够本身定义如下方法

if(!window.console){
 window.console = {}  console.log = function(str){    avalon.ready(function() {     var div = document.createElement("pre");     div.className = "mass_sys_log";     div.innerHTML = str + ""; //确保为字符串     document.body.appendChild(div);    });  } } 

上线后,将.mass_sys_log{ display: none; }

若是是高级浏览器,avalon会在控制台上打印许多调试消息,若是不想看到它们,能够这样屏蔽它们: avalon.config({debug: false})

其余要注意的地方(更新VM等)

avalon是经过Object.defineProperty来实现属性监听,所以不像angular,每次都VM的全部属性都比较一遍,而是用户一改动属性就当即触发视图变更。

这是avalon的优点,但隐藏一个很差的地方。用户必须在avalon.define的配置对象将全部监听的对象预先写好,不然没法监听到。

var vm =  avalon.define({
 $id: "test",  a: "aaa", //这个能够监听,改变它会同步视图  $b:"ccc" //这个是非监控属性,改变它不会同步视图 }) vm.c = '111' //这是avalon.define后才添加的,改变它不会同步视图    //在IE6-8还会抛错 

avalon在IE6-8是使用VBScript实现,不支持随便添加新属性,好比上例就会抛错

VBScript还带一个问题,它是不区分大小写,aaa与AAA是认为同样的,这时它会报重复定义的问题。

某些属性因为是VBscript的关键字,也不能做为属性或方法,不然在IE6-8也抛错,如type, err, erm, me

因为avalon是经过Object.defineProperty来实现属性监听,它会转换其底下的每个属性,每个数组,每个子对象,所以对象的层次不太深,不然会带来性能问题。最好不超过3层,数组的层次也宜过长。而且尽可能使用$开头或放在$skipArray减小无谓的属性监听。

avalon在1.5中 ,子对象再也不支持$watch, $fire方法,也再也不支持data-duplex-focus, data-duplex-observe辅助指令。

avalon能够经过如下手段撕开不能监听新属性的限制,就是重写子对象!

var vm =  avalon.define({
 $id: "test",  a: {} }) vm.a = { b: 2,//如今a.b, a.c, a.d能够与视图互相同步了 c: 3 d: 4 } 

若是你想重写数组的某一个元素,能够其set方法,若是交互两个数组元素,请使用splice方法或深拷贝原数组再赋值。

<!DOCTYPE html>
<html lang="zh-cn">  <head>   <meta charset="utf-8">   <script src="avalon.js"></script>   <script>    var vm = avalon.define({     $id: "test",     items: [      {       name: 'First lady'      },      {       name: 'Second boy'      },      {       name: 'Third guy'      }     ];    });    var temp = vm.items.splice(2, 1);    vm.items.splice(1, 0, vm[0].$model);//若是是简单数据类型,就不须要.$model   </script>  </head>  <body>   <ul ms-controller="test">    <li ms-repeat="items">{{ el.name }}</li>   </ul>  </body> </html> 
<!DOCTYPE html>
<html>  <head>   <title>avalon入门</title>   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">   <script src="avalon.js" type="text/javascript"></script>   <script>    var model = avalon.define({     $id: 'test',     arr: [{       'value': '0',       'text': 'zero'      }, {       'value': '1',       'text': 'one'      }, {       'value': '2',       'text': 'two'      }, {       'value': '3',       'text': 'three'      }],     click: function () {      var aa = avalon.mix(true, [], model.$model.arr)      //交换one和three      aa[1] = aa[3]      aa[3] = aa[1]      //控制台显示交换成功      console.log(aa[1])      console.log(aa[3])      model.arr = aa     }    })   </script>  </head>  <body>   <div ms-controller="test">    <button type="button" ms-click="click">xxx</button>    <ul>     <li ms-repeat="arr" >      {{el.text}}     </li>    </ul>   </div>  </body> </html> 

咱们能够在 官网 或 这里 看到更多有用的信息

相关文章
相关标签/搜索