一个项目是由许多人分工写的,所以必需要合理地拆散,因而有了模块化。体如今工做上,PM一般它这为某某版块,某某频道,某某页面。某一个模块,必须是包含其固有的数据,样式,HTML与处理逻辑。在jQuery时代,奉行的是“无侵入式javascript”,页面虽然是拆成一块块,但最后是经过PHP等后端模板合并起来,而且把第一屏的数据直接灌进去,接着是无尽的选择某些元素进行处理,选择某些元素进行处理。javascript里面是满屏的CSS表达式,若是不一一对着HTML页面,这是没法阅读的。换言之,jQuery很容易产生readyOnly的代码。javascript
avalon是引入分层构架,视图就是视图,数据就是数据,JS里面是操做数据,不会再操做视图,泾渭分明。视图,换言之就是最初作好的那些HTML片断,只须要在里面添加上ms-controller指令(或叫绑定属性),指定其将要做用的ViewModel的ID,而后在它里面添加其余绑定就好了。数据,特指是ViewModel,avalon是经过define方法定义,目的是实现“操做数据即操做DOM”,今后咱们再也用不上什么操做DOM的API,javascript代码量当即减小了一半以上,条理更清晰,更易维护。html
ViewModel的定义是一个重头戏。在入门教程里,是这样定义的:java
var model = avalon.define("test", function(vm) { vm.firstName = "司徒" vm.lastName = "正美" vm.fullName = {//一个包含set或get的对象会被当成PropertyDescriptor, set: function(val) {//里面必须用this指向scope,不能使用scope var array = (val || "").split(" "); this.firstName = array[0] || ""; this.lastName = array[1] || ""; }, get: function() { return this.firstName + " " + this.lastName; } } vm.arr = ["aaa", 'bbb', "ccc", "ddd"] vm.selected = ["bbb", "ccc"] vm.checkAllbool = vm.arr.length === vm.selected.length vm.checkAll = function() { if (this.checked) { vm.selected = vm.arr } else { vm.selected.clear() } } }) model.selected.$watch("length", function(n) { model.checkAllbool = n === model.arr.size() })
有两个参数,第一个定义ID,第二个是定义ViewModel自己的数据,它有什么监听属性啊,计算属性啊,一些特殊的指令啊,$watch回调啊用户须要区分vm与model的区别,有什么须要注意的地方(这些在入门教程都有介绍)。在1.3.3中,添加了如今这种新的定义方式,只要传入一个对象:git
var model = avalon.define({ $id: "test", firstName: "司徒", lastName: "正美", fullName: {//一个包含set或get的对象会被当成PropertyDescriptor, set: function(val) {//里面必须用this指向scope,不能使用scope var array = (val || "").split(" "); this.firstName = array[0] || ""; this.lastName = array[1] || ""; }, get: function() { return this.firstName + " " + this.lastName; } }, arr: ["aaa", 'bbb', "ccc", "ddd"], selected: ["bbb", "ccc"], checkAllbool: false, checkAll: function() { if (this.checked) { model.selected = model.arr } else { model.selected.clear() } } }) model.checkAllbool = model.arr.length === model.selected.length model.selected.$watch("length", function(n) { model.checkAllbool = n === model.arr.size() })
当咱们将一个对象传进avalon.define方法,它将返回一个全新的对象,它添加了许多$方法与属性,而且原来的属性都变得很是奇怪,在控制台下能够看到它们都对着一个set方法一个get方法。ecma262 v5称之为访问器属性(named accessor properties)。固然不一样的人有不一样的译法,你们想详细了解此属性的特性,能够阅读如下连接,这是avalon能让你修改属性就能同步视图的关键!github
注意,咱们全部定义的VM都存放在avalon.vmodels对象上。打开咱们上一节写的项目,在firebug下输入avalon.vmodels能够查看到:
web
那么为了应用这些ViewModel,咱们就须要用到ms-controller、 ms-important、ms-skip这三个指令。ms-controller在页面上表现为一个特殊的属性,其属性值为ViewModel的$id,表示将在此元素或其子孙元素上圈定它的做用域范围,但若是这些HTML存在它没有的属性,它能够向上查找上一级的ViewModel的属性。换言之,ms-controller能够互相套嵌的。 ms-important的用法与ms-controller差很少,但它不会向上查找。ms-skip注明这块区域不该用任何的ViewModel的属性,它里面的任何指令(绑定属性)都会失效。由于{{}}也算一种指令,而任何指令在被扫描后都会被移除,若是咱们想保留某个区域的{{}},就须要用到ms-skip。有关ms-controller, ms-important的详细用法可见这里。后端
上面的ViewModel再配合一些HTML代码,就是实现一些用jQuery很是费劲才能实现的功能api
<div ms-controller="test"> <p>First name: <input ms-duplex="firstName" /></p> <p>Last name: <input ms-duplex="lastName" /></p> <p>Hello, <input ms-duplex="fullName"></p> <div>{{firstName +" | "+ lastName }}</div> <ul> <li><input type="checkbox" ms-click="checkAll" ms-checked="checkAllbool"/>全选</li> <li ms-repeat="arr" ><input type="checkbox" ms-value="el" ms-duplex="selected"/>{{el}}</li> </ul> </div>
你们能够在这里看到实际运行效果。数组
再细说一下ViewModel(咱们一般也简称为VM)的一些属性。ruby
$id: VM的ID,方便在avalon.vmodels里查找到它,或用在ms-controller、ms-important上。
$events:里面存放着各类回调,它们是经过$watch方法添加的。
$watch:这是一个方法,有两个参数,第一个是VM中的某一个属性名,只能这个VM的直接子属性名,第二个是回调函数,当此属性发生改变时,就会执行此回调。回调里会依次传入它的新老属性值。
$unwatch:移除某个属性的回调。
$fire:手动触发此回调。
$accessors:放置与监听属性相连动的视图刷新函数,当咱们改变某一属性时,框架就会在这里找到对应的视图刷新函数,传入当前值,实现对视图的同步。
$123323213:它的格式是$加上一串数字,它是用于放置监控数组的视图刷新函数,当咱们调用监控数组的方法时,框架就此根据当前数组的个数与排列顺序,从新渲染对应的区域。它与$accessors同样,不开放给用户调用的。
$model:就是ViewModel的净化版,没有$XXX属性,访问器属性所有还原为普通属性,专门用于提交到后台用。固然咱们提交后台,还须要用JSON.parse(JSON.stringify(VM.$model))处理一下,将里面的函数干掉。
如今说的仍是基本用法,$watch、$unwatch、 $fire其实远远比你想象的强大,你们感兴趣的话,能够到这里了解其高级用法。
好了,咱们把上面的代码放进上一节,修改aaa.js, aaa.html,感觉一下一个复杂的ViewModel的应用吧。
有了ViewModel后,咱们的代码就显得很是有内聚力,本身知道要做用于视图的哪一块区域,而且不用本身操心如此修改DOM,变成单纯的数据操做。
而数据操做是须要在页面定义一些指令(咱们称之为绑定属性与插值表达式)。如今最简单的有两个,{{ prop }}是直接将属性输出到页面,若是它存在尖括号,会原样输出,不会转换为HTML标签。{{ prop | html}}则相反,好比这个属性的值为”xxxxerer”,那么里面就真会转为一个b标签。其实{{prop}},{{prop|html}}还有另外一种写法, ms-text=”prop”, ms-html=”prop”。有关这些绑定的属性详细用法,咱们下一节讲述。
本章节的代码能够从这里下载。