Transcript avalon

前端MVVM的应用
By 司徒正美
国外比较流行的MVVM
knockout
WinJS
ember
angular
kendoui
montag
ejs
官网链接
•
•
•
•
•
•
http://knockoutjs.com/
http://emberjs.com/
http://angularjs.org/
http://www.kendoui.com/
http://rivetsjs.com/
http://montagejs.org/
国内的MVVM
avalon
目前好像就我一个人在搞?
WHY?
世界总是在进化
Prototype.js 对语言本身进行修复
jQuery 对DOM进行跨浏览器兼容性处理
Backbone 为前端引入“架构”的概念,对絮乱的JS代码进行组织
MVVM —— 。。。。。
1. 立足的时代不同
2. 面临的问题不同
Prototype
• JS性能奇差,原生函数数量缺乏,
语言的特征尚待发掘
• Function.prototype.bind的发
明, 类的模拟, AJAX应用
jQuery
•
Firefox借尸还运魂,致力于为
JS添加更多新方法,新特征。
•
浏览器商划分为IE与W3C两大
阵营,DOM兼容变得可行而迫
切。
•
brower sniff 淡出 feture
detect流行,人们开始发掘
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
异步管理
• 回调地狱
• 异步中的出错捕获
Mochikit Deferred
Dojo Deferred
JSDeferred (巅峰之作, 包含未来Promise的所有隐藏元素)
jQuery Deferred(不完全的Promise)
When.js
Q.js
RSVP
mmDeferred
Deferred的要求,异常总是被
捕获,失败了转入出错列队,处
理后继续转回正常列队第一次触
发总是异步的
http://pythonhosted.org/defer/d
efer.html
Promise/A的要求:触发回调的对象Deferred 与接受回调的对象Promise要分离,存
在then方法来接受正常回调,出错回调与通知回调,then位于Promise上,并总
返回新的Promise
因此最佳的实践由Deferred对象构成三条单链
而jQuery是三条函数数组,重用一个Promise,只是改变状态,并在抛错时死无全尸
前端模板,加载器, Deferred/Promise
维护性
但最关键的问题没有解决
•
•
•
•
JS代码与HTML的强耦合
JS代码必须在页面出来才能动手
眼花缭乱的选择器
write less do more  堆得快,死得快  豆腐渣工程
MVVM的产生
微软2005年左右发明 WFP
1. 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定
到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候
View也可以不变。
2. 可重用性:你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这
段视图逻辑。
3. 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员
可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代
码。
4. 可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
对VM的操作成为JS编程的核心
var M= {
firstName: “”,
lastName: “”,
fulllName: “”
}
knockout
function ViewModel(){
this.firstName = ko.observable('Bob');
this.lastName = ko.observable('Smith');
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
}
var p = new ViewModel
console.log(p.firstName())
console.log(p.lastName())
console.log(p.fullName())
ember
App = Ember.Application.create();
App.Person = Ember.Object.extend({
firstName: null,
lastName: null,
fullName: function() {
return this.get('firstName') +
" " + this.get('lastName');
}.property('firstName', 'lastName')
});
var p = 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"))
angular
var app = angular.module('plunker', []);
//无法在外面操作VM
app.controller('MainCtrl', function($scope) {
$scope.firstName = 'Phil';
$scope.lastName = 'Roberts'
//https://github.com/angular/angular.js/issues/1659
$scope.$watch('[firstName, lastName]', function(newVal) {
$scope.fullName = newVal[0] + " " + newVal[1];
})
});
avalon
var a = avalon.define("xxx", function(vm) {
vm.firstName = "司徒"
vm.lastName = "正美"
vm.fullName = {
get: function() {
return this.firstName + " " + this.lastName
}
}
})
console.log(a);
console.log(a.firstName)
console.log(a.lastName)
console.log(a.fullName)
VM 中的属性以某种机制触发 视图对应区域进行更新,因此JS代码可以做到看
不到一行DOM代码,让我们专致于业务开发
如果通知视图更新?
angular 内部的set, get
knockout 属性变方法
emberjs 万能set, get
avalon.js 利用Object.defineProperty, VBScript重载等于号(=)
• 外国有文章介绍,使用了AngularJS(MVVM)代替(Backbone),代码减少了
一半。
http://www.localytics.com/blog/2013/angularjs-at-localytics/
• 利用avalon 实现一个简单的成绩单
• http://www.cnblogs.com/rubylouvre/p/3213430.html#2735795
外国MVVM的流程情况
http://stackoverflow.com/
9.268 angular
7,079 knockout
5,305 emberjs
http://www.slideshare.net/
53,341 angularjs
10,633 ember
4,672 knockout
如何与V同步
• 核心——观察者模式
• VM中的监控属性与计算属性都对应一个数组
• 计算属性在第一次执行,将自己放进它所依赖的监控属性的数组中(依赖收集)
• 视图刷新函数在扫描时动态生成注入到这些数组中(依赖收集),当属性变化时,
执行这些函数刷新函数
更多细节
• Avalon, angular, knockout会自动收集依赖
• avlaon通过赋值语句,取值语句(var a = b)触发
• Angular对函数进行parse
• Knockout执行同名函数(属性变属性)
视图中的绑定属性
ms-click=“update”
ms-text=“firstName”
ng- model="todo.done"
ng-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.
3.
4.
5.
视图刷新函数依赖于 求值函数
求值函数依赖于 VM对应属性
VM对应属性的订阅数组装着视图刷新函数
VM属性改变,遍历执行订阅数组的函数
更新视图
双向绑定链的原理2
V 对VM 的更新
1. Angular的ng-model, avalon的ms-duplex,knockout的value binding会对
所在元素绑定change, input, click, perpertychange等事件
2. 事件回调大抵为
function (){
vm.xxx = this.value }
动态模板与静态模板
• 动态模板基于DOM
的 DOM 树,整个
页面
• 静态模板基于字符串
 script, textara
标签
UnitedStack收各路好汉啦!
静态模板
• 雕版印刷 不灵活,每次替换一大块HTML,破坏事件绑定
动态模板
• 绑定属性与{{}}插值表达式让DOM树变成模板
• 活字印刷每次只更新需更新的内容,文本节点的nodeValue,特性节点的value,
样式属性的某个值(最小化刷新)
数据绑定不单单是绑定数据
• 数据填充
• 显示隐藏
• 循环生成
• 类名添加移除
• 样式操作
• 事件绑定
• 引入局部模板
• ……
完成jQuery 90%的事
于是实现一个切换卡就这么简单
没有DOM的世界真美妙
DOM的世界(无尽的未知)
纯JS的世界(井然有序)
VM的作用
MVVM框架都集成最前沿的技术解决方案——
JS parser 求值函数
HTML5新接口classList,dataSet
required hidden, forEach, bind, History 新API, 动画事件…
Promise winjs, emberjs, angular
路由系统
状态机
缓存系统。。。。。
读它们的源码,能比读jQuery的学得更多!
Over,THX
1.
问答时间
2. 广告时间
简历投递地址还是:[email protected],期待你的邮件,
我尽量保证每份邮件都回复。