迷你MVVM框架 avalonjs 入门教程.doc_第1页
迷你MVVM框架 avalonjs 入门教程.doc_第2页
迷你MVVM框架 avalonjs 入门教程.doc_第3页
迷你MVVM框架 avalonjs 入门教程.doc_第4页
迷你MVVM框架 avalonjs 入门教程.doc_第5页
已阅读5页,还剩74页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1. 关于AvalonJs2. 开始的例子3. 扫描4. 视图模型5. 数据模型6. 绑定7. 作用域绑定(ms-controller, ms-important)8. 忽略扫描绑定(ms-skip)9. 模板绑定(ms-include)10. 数据填充(ms-text, ms-html)11. 类名切换(ms-class, ms-hover, ms-active)12. 事件绑定(ms-on,)13. 显示绑定(ms-visible)14. 插入绑定(ms-if)15. 双工绑定(ms-duplex)16. 样式绑定(ms-css)17. 数据绑定(ms-data)18. 属性绑定(ms-attr)19. 循环绑定(ms-repeat)20. 数组循环绑定(ms-each废弃)21. 对象循环绑定(ms-with废弃)22. UI绑定(ms-widget)23. 模块间通信及属性监控 $watch,$fire, $unwatch24. 过滤器25. AMD加载器26. 路由系统27. AJAX28. 功能扩展29. 在IE6下调试avalon30. 权限控制关于AvalonJSavalon是一个简单易用迷你的MVVM框架,它最早发布于2012.09.15,为解决同一业务逻辑存在各种视图呈现而开发出来的。 事实上,这问题其实也可以简单地利用一般的前端模板加jQuery 事件委托 搞定,但随着业务的膨胀, 代码就充满了各种选择器与事件回调,难以维护。因此彻底的将业务与逻辑分离,就只能求助于架构。 最初想到的是MVC,尝试过backbone,但代码不降反升,很偶尔的机会,碰上微软的WPF, 优雅的MVVM架构立即吸引住我,我觉得这就是我一直追求的解决之道。MVVM将所有前端代码彻底分成两部分,视图的处理通过绑定实现(angular有个更炫酷的名词叫指令), 业务逻辑则集中在一个个叫VM的对象中处理。我们只要操作VM的数据,它就自然而然地神奇地同步到视图。 显然所有神秘都有其内幕,C#是通过一种叫访问器属性的语句实现,那么JS也有没有对应的东西。 感谢上帝,IE8最早引入这东西(Object.defineProperty),可惜有BUG,但带动了其他浏览器实现它,IE9+便能安全使用它。 对于老式IE,我找了好久,实在没有办法,使用VBScript实现了。Object.defineProperty或VBS的作用是将对象的某一个属性,转换一个setter与getter, 我们只要劫持这两个方法,通过Pub/Sub模式就能偷偷操作视图。为了纪念WPF的指引,我将此项目以WPF最初的开发代号avalon来命名。 它真的能让前端人员脱离DOM的苦海,来到数据的乐园中!优势绝对的优势就是降低了耦合, 让开发者从复杂的各种事件中挣脱出来。 举一个简单地例子, 同一个状态可能跟若干个事件的发生顺序与发生时的附加参数都有关系, 不用 MVC (包括 MVVM) 的情况下, 逻辑可能非常复杂而且脆弱。 并且通常需要在不同的地方维护相关度非常高的一些逻辑, 稍有疏忽就会酿成 bug 不能自拔。使用这类框架能从根本上降低应用开发的逻辑难度, 并且让应用更稳健。除此之外, 也免去了一些重复的体力劳动, 一个 value 就代替了一行 $(selector).text(value)。 一些个常用的 directive 也能快速实现一些原本可能需要较多代码才能实现的功能 使用简单,作者是吃透了knockout, angular,rivets API设计出来,没有太多复杂的概念, 指令数量控制得当,基本能覆盖所有jQuery操作, 确保中小型公司的菜鸟前端与刚转行过来的后端也能迅速上手。 兼容性非常好, 支持IE6+,firefox3.5+, opera11+, safari5+, chrome4, 最近也将国产的山寨浏览器(360, QQ, 搜狗,猎豹, 邀游等)加入兼容列队 (相比其他MVVM框架,KnockoutJS(IE6), AngularJS1.3(IE9), EmberJS(IE8), WinJS(IE9)) 向前兼容非常好,不会出现angular那种跳崖式升级 注重性能,由于avalon一直在那些上千的大表格里打滚,经历长期的优化, 它能支撑14000以上绑定(相对而言,angular一个页面只能放2000个绑定)。另,在IE10等能良好支持HTML5的浏览器, 还提供了avalon.modern.js这个高性能的版本。 没有任何依赖,不到5000行,压缩后不到50KB 完善的单元测试,由于测试代码非常庞大,放在独立的仓库中avalon.test 拥有一个包含2个Grid,1个树,1 个验证插件等总数近50个UI组件库OniUI, 由去哪儿网前端架构组在全力开发与维护 存在一个活跃的小社区,由于国内已有不少公司在用,我们都集中一个QQ群里互相交流帮助 QQ:79641290、228372837(注明来学avalon) 支持管道符风格的过滤函数,方便格式化输出 让DOM操作的代码近乎绝迹,因此实现一个功能,大致把比jQuery所写的还要少50% 使用类似CSS的重叠覆盖机制,让各个ViewModel分区交替地渲染页面 节点移除时,智能卸载对应的视图刷新函数,节约内存 操作数据即操作DOM,对ViewModel的操作都会同步到View与Model去 自带AMD模块加载器,省得与其他加载器进行整合avalon现在有三个分支:avalon.js 兼容IE6,标准浏览器, 及主流山寨浏览器(QQ, 猎豹, 搜狗, 360, 傲游); avalon.modern.js 则只支持IE10等支持HTML5现代浏览器 ; avalon.mobile.js,添加了触屏事件与fastclick支持,用于移动端开始的例子我们从一个完整的例子开始认识 avalon : w x h W: H: var vm = avalon.define($id: box,w: 100,h: 100,click: function() vm.w = parseFloat(vm.w) + 10;vm.h = parseFloat(vm.h) + 10;)上面的代码中,我们可以看到在JS中,没有任何一行操作DOM的代码,也没有选择器,非常干净。在HTML中, 我们发现就是多了一些以ms-开始的属性与标记,有的是用于渲染样式, 有的是用于绑定事件。这些属性或标记,实质就是avalon的绑定系统的一部分。绑定(有的框架也将之称为指令), 负责帮我们完成视图的各种操作,相当于一个隐形的jQuery。正因为有了绑定,我们就可以在JS代码专注业务逻辑本身, 写得更易维护的代码!扫描不过上面的代码并不完整,它能工作,是因为框架默认会在DOMReady时扫描DOM树,将视图中的绑定属性与插值表达式抽取出来,转换为求值函数与视图刷新函数。上面的JS代码相当于:avalon.ready(function() varvm = avalon.define($id: box,w: 100,h: 100,click: function() vm.w = parseFloat(vm.w) + 10;vm.h = parseFloat(vm.h) + 10;)avalon.scan()avalon.scan是一个非常重要的方法,它有两个可选参数,第一个是扫描的起点元素,默认是HTML标签,第2个是VM对象。/源码avalon.scan = function(elem, vmodel) elem = elem | rootvarvmodels = vmodel ? .concat(vmodel) : scanTag(elem, vmodels)视图模型视图模型,ViewModel,也经常被略写成VM,是通过avalon.define方法进行定义。生成的对象会默认放到avalon.vmodels对象上。 每个VM在定义时必须指定$id。如果你有某些属性不想监听,可以直接将此属性名放到$skipArray数组中。varvm = avalon.define($id: test,a: 111,b: 222,$skipAarray: b,$c: 333,firstName: 司徒,lastName: 正美,fullName: /一个包含set或get的对象会被当成PropertyDescriptor,set: function(val) /里面必须用this指向scope,不能使用scopevararray = (val | ).split( );this.firstName = array0 | ;this.lastName = array1 | ;,get: function() returnthis.firstName + + this.lastName;,array: 1,2,3,array2:e: 1, e: 2d: k: 111,$skipArray: f,f: 2222)接着我们说一些重要的概念: $id, 每个VM都有$id,如果VM的某一个属性是对象(并且它是可监控的),也会转换为一个VM,这个子VM也会默认加上一个$id。 但只有用户添加的那个最外面的$id会注册到avalon.vmodels对象上。 监控属性,一般地,VM中的属性都会转换为此种属性,当我们以vm.aaa = yyy这种形式更改其值时,就会同步到视图上的对应位置上。 计算属性,定义时为一个对象,并且只存在set,get两个函数或只有一个get一个函数。它是监控属性的高级形式,表示它的值是通过函数计算出来的,是依赖于其他属性合成出来的。 监控数组,定义时为一个数组,它会添加了许多新方法,但一般情况下与普通数组无异,但调用它的push, unshift, remove, pop等方法会同步视图。 非监控属性,这包括框架添加的$id属性,以$开头的属性,放在$skipArray数组中的属性,值为函数、元素节点、文本节点的属性,总之,改变它们的值不会产生同步视图的效果。$skipArray 是一个字符串数组,只能放当前对象的直接属性名,想禁止子对象的某个属性的监听,在那个子对象上再添加一个$skipAray数组就行了。视图里面,我们可以使用ms-controller, ms-important指定一个VM的作用域。此外,在ms-each, ms-with,ms-repeat绑定属性中,它们会创建一个临时的VM,我们称之为代理VM, 用于放置$key, $val, $index, $last, $first, $remove等变量或方法。另外,avalon不允许在VM定义之后,再追加新属性与方法,比如下面的方式是错误的:varvm = avalon.define($id: test, test1: 点击测试按钮没反应 绑定失败;);vm.one = function() vm.test1 = 绑定成功;也不允许在define里面直接调用方法或ajaxavalon.define(test, function(vm)alert(111) /这里会执行两次$.ajax( /这里会发出两次请来async:false,type: post,url: sdfdsf/fdsfds/dsdd,success: function(data)console.log(data)avalon.mix(vm, data)应该改成:varvm = avalon.define($id: test,aaa: , /这里应该把所有AJAX都返回的数据都定义好bbb: ,)$.ajax( /这里会发出两次请来async:false,type: post,url: sdfdsf/fdsfds/dsdd,success: function(data)for(vari indata)if(vm.hasOwnProperty(i)vmi = datai)我们再看看如何更新VM中的属性(重点):var model : avalon.define($id: update, aaa : str,bbb : false,ccc : 1223,time : new Date,simpleArray : 1, 2, 3, 4,objectArray : name: a, name: b, name: c, name: d,object : o1: k1,o2: k2,o3: k3,simpleArray : 1, 2, 3, 4,objectArray : name: a, value: aa, name: b, value: bb, name: c, value: cc, name: d, value: dd,object : o1: k1,o2: k2,o3: k3)setTimeout(function() /如果是更新简单数据类型(string, boolean, number)或Date类型model.aaa = 这是字符串model.bbb = truemodel.ccc = 999999999999var date = new Datemodel.time = new Date(date.setFullYear(2005), 2000)setTimeout(function() /如果是数组,注意保证它们的元素的类型是一致的/只能全是字符串,或是全是布尔,不能有一些是这种类型,另一些是其他类型/这时我们可以使用set方法来更新(它有两个参数,第一个是index,第2个是新值)model.simpleArray.set(0, 1000)model.simpleArray.set(2, 3000)model.objectArray.set(0, name: xxxxxxxxxxxxxxxx, value: xxx), 2500)setTimeout(function() model.objectA = 5555, 3000)setTimeout(function() /如果要更新对象,直接赋给它一个对象,注意不能将一个VM赋给它,可以到VM的$model赋给它(要不会在IE6-8中报错)model.object = aaaa: aaaa,bbbb: bbbb,cccc: cccc,dddd: dddd, 3000)aaabbbccctime | date(yyyy - MM - dd mm:ss)el $key $val这里还有个例子,大家认真看看。绑定avalon的绑定(或指令),拥有以下三种类型: 插值表达式, 这是开标签与闭标签间,换言之,也是位于文本节点中,innerText里。里面可以添加各种过滤器(以|进行标识)。值得注意的是实际是文本绑定(ms-text)的一种形式。 ms-*绑定属性, 这是位于开标签的内部, 95%的绑定都以这种形式存在。 它们的格式大概是这样划分的ms + type + - + param1 + - + param1 + - + param2 + . + number = valuems-skip /这个绑定属性没有值ms-controller=expr/这个绑定属性没有参数ms-if=expr/这个绑定属性没有参数ms-if-loop=expr/这个绑定属性有一个参数ms-repeat-el=array/这个绑定属性有一个参数ms-attr-href=xxxx/这个绑定属性有一个参数ms-attr-src=xxx/a/yyy/b/这个绑定属性的值包含插值表达式,注意只有少部分表示字符串类型的属性可以使用插值表达式ms-click-1=fn/这个绑定属性的名字最后有数字,这是方便我们绑定更多点击事件 ms-click-2=fn ms-click-3=fn ms-on-click=fn/只有表示事件与类名的绑定属性的可以加数字,如这个也可以写成 ms-on-click-0=fn ms-class-1=xxxms-class-2=yyyms-class-3=xxx/数字还表示绑定的次序ms-css-background-color=xxx/这个绑定属性有两个参数,但在css绑定里,相当于一个,会内部转换为backgroundColor ms-duplex-aaa-bbb-string=xxx/这个绑定属性有三个参数,表示三种不同的拦截操作 data-xxx-yyy=xxx,辅助指令,比如ms-duplex的某一个辅助指令为data-duplex-event=change,ms-repeat的某一个辅助指令为data-repeat-rendered=yyy作用域绑定(ms-controller, ms-important)如果一个页面非常复杂,就需要划分模块,每个模块交由不同的ViewModel去处理。我们就要用到ms-controller与ms-important来指定ViewModel了。我们看下面的例子:HTML结构name : colorname : colorname : colorname : colorViewModelavalon.ready(function() avalon.define($id: AAA,name: liger,color: green);avalon.define($id: BBB,name: sphinx,color: red);avalon.define($id: CCC,name: dragon /不存在color);avalon.define($id: DDD,name: sirenia /不存在color);avalon.scan()name : colorname : colorname : colorname : color可以看出ViewModel在DOM树的作用范围其实与CSS很相似,采取就近原则,如果当前ViewModel没有此字段 就找上一级ViewModel的同名字段,这个机制非常有利于团队协作。如果从另一个角度来看,由于这种随机组成的方式就能实现类似继承的方式,因此我们就不必在JS代码时构建复杂的继承体系。类的继承体系是源自后端复杂业务的膨胀而诞生的。早在20世界80年代初期,也就是面向对象发展的初期,人们就非常看重继承这个概念。 继承关系蕴涵的意义是非常深远的。使用继承我们可以基于差异编程,也就是说,对于一个满足我们大部分需求的类,可以创建一个它的子类,重载它个别方法来实现我们所要的功能。只子继承一个类, 就可以重类该类的代码!通过继承,我们可以建立完整的软件结构分类,其中每一个层都可以重用该层次以上的代码。这是一个美丽新世界。但类继承的缺点也是很明显的,在下摘录一些:面向对象语言与生俱来的问题就是它们与生俱来的这一整个隐性环境。你想要一根香蕉,但你得到的是一头手里握着香蕉的大猩猩,以及整个丛林。 - Joe Armstrong在适合使用复合模式的共有类中使用继承,会把这个类与它的超类永远地束缚在一起,从而人为地限制了子类的性能类继承的缺点1. 超类改变,子类要跟着改变,违反了“开闭”原则2. 不能动态改变方法实现,不能在运行时改变由父类继承来的实现3. 破坏原有封装,因为基类向子类暴露了实现细节4. 继承会导致类的爆炸因此在选择是继承还是组合的问题上,avalon倾向组合。组合的使用范例就是CSS,因此也有了ms-important的诞生。而ms-important就相当于CSS的important语句,强制这个区域使用此ViewModel,不再往上查找同名属性或方法!另,为了避免未经处理的原始模板内容在页面载入时在页面中一闪而过,我们可以使用以下样式(详见这里):.ms-controller,.ms-important,ms-controller,ms-importantvisibility: hidden;忽略扫描绑定(ms-skip)这是ms-skip负责。只要元素定义了这个属性,无论它的值是什么,它都不会扫描其他属性及它的子孙节点了。$index - numA:yy模板绑定(ms-include)如果单是把DOM树作为一个模板远远不够的,比如有几个地方,需要重复利用一套HTML结构,这就要用到内部模板或外部模板了。内部模板是,这个模板与目标节点是位于同一个DOM树中。我们用一个MIME不明的script标签或者noscript标签(0.94后支持,建议使用它)保存它,然后通过ms-include=id引用它。avalon.define($id: test,xxx: 引入内部模板)here, 3 + 6 * 5 xxx注意,ms-include的值要用引号括起,表示这只是一个字符串,这时它就会搜索页面的具有此ID的节点,取其innerHTML,放进ms-include所在的元素内部。否则这个tpl会被当成一个变量, 框架就会在VM中检测有没有此属性,有就取其值,重复上面的步骤。如果成功,页面会出现here, 2的字样。如果大家想在模板加载后,加工一下模板,可以使用data-include-loaded来指定回调的名字。如果大家想在模板扫描后,隐藏loading什么的,可以使用data-include-rendered来指定回调的名字。由于ms-include绑定需要定义在一个元素节点上,它的作用仅仅是一个占位符,提供一个插入位置的容器。 如果用户想在插入内容后,去掉这容器,可以使用data-include-replace=true。下面是它们的实现var vmodels = data.vmodelsvar rendered = getBindingCallback(elem.getAttribute(data-include-rendered), vmodels)var loaded = getBindingCallback(elem.getAttribute(data-include-loaded), vmodels)function scanTemplate(text) if (loaded) text = loaded.apply(elem, text.concat(vmodels)avalon.innerHTML(elem, text)scanNodes(elem, vmodels)rendered & checkScan(elem, function() rendered.call(elem)外部模板,通常用于多个页面的复用,因此需要整成一个独立的文件。这时我们就需要通过ms-include-src=src进行加载。比如有一个HTML文件tmpl.html,它的内容为:这是一个独立的页面它是通过AJAX的GET请求加载下来的然后我们这样引入它有关它的高级应用的例子可见这里利用ms-include与监控数组实现一个树注意,ms-include-src需要后端服务器支持,因为用到同域的AJAX请求。数据填充(ms-text, ms-html)这分两种:文本绑定与HTML绑定,每种都有两个实现方式avalon.define($id: test,text: 1111 )用于测试是否被测除xxxxtextyyyy用于测试是否被测除xxxxtext|htmlyyyy用于测试是否被测除xxxx yyyy用于测试是否被测除xxxx yyyy默认情况下,我们是使用 进行插值,如果有特殊需求,我们还可以配置它们:avalon.config(interpolate:)注意,大家不要用 作为插值表达式的界定符,因为在IE6-9里可能转换为注释节点,详见这里插值表达式在绑定属性的使用,只限那些能返回字符串的绑定属性,如ms-attr、ms-css、ms-include、ms-class、 ms-href、 ms-title、ms-src等。一旦出现插值表达式,说明这个整个东西分成可变的部分与不可变的部分,内为可变的,反之亦然。 如果没有说明整个东西都要求值,又如ms-include=id,要用两种引号强制让它的内部不是一个变量。类名切换(ms-class, ms-hover, ms-active)avalon提供了多种方式来绑定类名,有ms-class, ms-hover, ms-active, 具体可看这里事件绑定(ms-on)avalon通过ms-on-click或ms-click进行事件绑定,并在IE对事件对象进行修复,具体可看这里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-scroll ms-animation ms-on-*有关事件回调传参avalon.ready(function() avalon.define($id: simple, firstN

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论