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],期待你的邮件,
我尽量保证每份邮件都回复。