前端MVVM的应用By司徒正美国外比较流行的MVVMknockoutemberWinJSkendouiangularmontagejs••••••官网链接目前好像就我一个人在搞?国内的MVVMavalonWHY?世界总是在进化Prototype.js对语言本身进行修复jQuery对DOM进行跨浏览器兼容性处理Backbone为前端引入“架构”的概念,对絮乱的JS代码进行组织MVVM——。。。。。1.立足的时代不同2.面临的问题不同Prototype•JS性能奇差,原生函数数量缺乏,语言的特征尚待发掘•Function.prototype.bind的发明,类的模拟,AJAX应用jQuery•Firefox借尸还运魂,致力于为JS添加更多新方法,新特征。•浏览器商划分为IE与W3C两大阵营,DOM兼容变得可行而迫切。•browersniff淡出feturedetect流行,人们开始发掘DOM的宝藏。jQuery时代的问题•过度于依赖选择器频繁遍历DOM树;•然后利用DSL式智能API频繁操作元素;•DOM树就像一片变得越来越肥沃的土地;•jQuery插件就像一辆辆耕作的拖拉机;•于是出现撞车问题;后jQuery时代的问题•移动端的崛起•手机浏览器的性能与IE6持平,各种奇异的BUG,•jQuery对插件管理的失控——$.sub的出现与移除Backbone——后端MVC的简单移植•引入类管理•引入路由系统•引入历史管理•依重事件代理弥消前端模板对DOM树结构的破坏•既要处理业务逻辑•又要随时同步视图•还要处理AJAX带来的历史管理问题•看似什么都做,但什么也没做•代码越写越多•三个小和尚被一大堆SB整死的故事重演大失败!此时期的其他实践•前端模板的流行•加载器•由于Deferred发展出来的Promise规范字符串拼接requireJS与seajs•依赖管理,并行加载,别名机制,shim机制,包机制。。。数以百计的前端模板不使用with:mmTemplate,artTemplate,handlebars,mustache,juicer,doT低逻辑:mustache使用with:tmpl,yayaTemplate,aceTemplate,underscoreTemplate,kissyTemplate不使用定界符:aceTemplate异步管理•回调地狱•异步中的出错捕获MochikitDeferredDojoDeferredJSDeferred(巅峰之作,包含未来Promise的所有隐藏元素)jQueryDeferred(不完全的Promise)When.jsQ.jsRSVPmmDeferredDeferred的要求,异常总是被捕获,失败了转入出错列队,处理后继续转回正常列队第一次触发总是异步的的要求:触发回调的对象Deferred与接受回调的对象Promise要分离,存在then方法来接受正常回调,出错回调与通知回调,then位于Promise上,并总返回新的Promise因此最佳的实践由Deferred对象构成三条单链而jQuery是三条函数数组,重用一个Promise,只是改变状态,并在抛错时死无全尸前端模板,加载器,Deferred/Promise维护性但最关键的问题没有解决•JS代码与HTML的强耦合•JS代码必须在页面出来才能动手•眼花缭乱的选择器•writelessdomore堆得快,死得快豆腐渣工程MVVM的产生微软2005年左右发明WFP1.低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。2.可重用性:你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。3.独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用ExpressionBlend可以很容易设计界面并生成xaml代码。4.可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。对VM的操作成为JS编程的核心varM={firstName:“”,lastName:“”,fulllName:“”}knockoutfunctionViewModel(){this.firstName=ko.observable('Bob');this.lastName=ko.observable('Smith');this.fullName=ko.computed(function(){returnthis.firstName()++this.lastName();},this);}varp=newViewModelconsole.log(p.firstName())console.log(p.lastName())console.log(p.fullName())emberApp=Ember.Application.create();App.Person=Ember.Object.extend({firstName:null,lastName:null,fullName:function(){returnthis.get('firstName')++this.get('lastName');}.property('firstName','lastName')});varp=App.Person.create({firstName:Tom,lastName:Dale})console.log(p)console.log(p.get(firstName))console.log(p.get(lastName))console.log(p.get(fullName))angularvarapp=angular.module('plunker',[]);//无法在外面操作VMapp.controller('MainCtrl',function($scope){$scope.firstName='Phil';$scope.lastName='Roberts'//('[firstName,lastName]',function(newVal){$scope.fullName=newVal[0]++newVal[1];})});avalonvara=avalon.define(xxx,function(vm){vm.firstName=司徒vm.lastName=正美vm.fullName={get:function(){returnthis.firstName++this.lastName}}})console.log(a);console.log(a.firstName)console.log(a.lastName)console.log(a.fullName)VM中的属性以某种机制触发视图对应区域进行更新,因此JS代码可以做到看不到一行DOM代码,让我们专致于业务开发如果通知视图更新?angular内部的set,getknockout属性变方法emberjs万能set,getavalon.js利用Object.defineProperty,VBScript重载等于号(=)•外国有文章介绍,使用了AngularJS(MVVM)代替(Backbone),代码减少了一半。•利用avalon实现一个简单的成绩单•外国MVVM的流程情况://如何与V同步•核心——观察者模式•VM中的监控属性与计算属性都对应一个数组•计算属性在第一次执行,将自己放进它所依赖的监控属性的数组中(依赖收集)•视图刷新函数在扫描时动态生成注入到这些数组中(依赖收集),当属性变化时,执行这些函数刷新函数更多细节•Avalon,angular,knockout会自动收集依赖•avlaon通过赋值语句,取值语句(vara=b)触发•Angular对函数进行parse•Knockout执行同名函数(属性变属性)视图中的绑定属性ms-click=“update”ms-text=“firstName”ng-model=todo.doneng-bind-template={{salutation}}{{name}}!“data-bind=“html:aaa”data-bind=value:someValue,valueUpdate:'afterkeydown'data-bind=with:coords“{{}}text绑定•绑定属性必然包含两个东西:•操作类型与VM中的某个元素或JS表达式ms-visible=“toggle”visible表明是visible绑定,通过对元素display值进行显示隐藏,toggle为某个VM的属性Visible会对应某一个内部的视图刷新函数放进toggle对应的订阅数组中Toggle会转换为一个求值函数双向绑定链的原理VM对V的刷新1.视图刷新函数依赖于求值函数2.求值函数依赖于VM对应属性3.VM对应属性的订阅数组装着视图刷新函数4.VM属性改变,遍历执行订阅数组的函数5.更新视图双向绑定链的原理2V对VM的更新1.Angular的ng-model,avalon的ms-duplex,knockout的valuebinding会对所在元素绑定change,input,click,perpertychange等事件2.事件回调大抵为function(){vm.xxx=this.value}动态模板与静态模板•动态模板基于DOM的DOM树,整个页面•静态模板基于字符串script,textara标签UnitedStack收各路好汉啦!静态模板•雕版印刷不灵活,每次替换一大块HTML,破坏事件绑定动态模板•绑定属性与{{}}插值表达式让DOM树变成模板•活字印刷每次只更新需更新的内容,文本节点的nodeValue,特性节点的value,样式属性的某个值(最小化刷新)•数据填充•显示隐藏•循环生成•类名添加移除•样式操作•事件绑定•引入局部模板•……完成jQuery90%的事数据绑定不单单是绑定数据于是实现一个切换卡就这么简单没有DOM的世界真美妙DOM的世界(无尽的未知)纯JS的世界(井然有序)VM的作用MVVM框架都集成最前沿的技术解决方案——JSparser求值函数HTML5新接口classList,dataSetrequiredhidden,forEach,bind,History新API,动画事件…Promisewinjs,emberjs,angular路由系统状态机缓存系统。。。。。读它们的源码,能比读jQuery的学得更多!Over,THX1.问答时间2.广告时间简历投递地址还是:hui@unitedstack.com,期待你的邮件,我尽量保证每份邮件都回复。