前端MVVM框架avalon - 模型转换1
2013-06-21 14:19
267 查看
轻量级前端MVVM框架avalon-模型转换(一)
接上一章modelFactory工厂是如何加工用户定义的VM?
附源码
[align=left]洋洋洒洒100多行内部是魔幻般的实现[/align]
functionmodelFactory(scope){
varskipArray=scope.$skipArray,//要忽略监控的属性名列表
model={},
Descriptions={},//内部用于转换的对象
json={},
callSetters=[],
callGetters=[],
VBPublics=Object.keys(watchOne);//用于IE6-8
skipArray=Array.isArray(skipArray)?skipArray.concat(VBPublics):VBPublics;
forEach(scope,function(name,value){
if(!watchOne[name]){
json[name]=value;
}
varvalueType=avalon.type(value);
if(valueType==="Function"){
VBPublics.push(name);//函数无需要转换
}else{
if(skipArray.indexOf(name)!==-1){
returnVBPublics.push(name);
}
if(name.charAt(0)==="$"&&!systemOne[name]){
returnVBPublics.push(name);
}
varaccessor,oldArgs;
if(valueType==="Object"&&typeofvalue.get==="function"&&Object.keys(value).length<=2){
varsetter=value.set,
getter=value.get;
accessor=function(neo){//创建计算属性
if(arguments.length){
if(stopRepeatAssign){
return;//阻止重复赋值
}
if(typeofsetter==="function"){
setter.call(model,neo);
}
if(oldArgs!==neo){//由于VBS对象不能用Object.prototype.toString来判定类型,我们就不做严密的检测
oldArgs=neo;
notifySubscribers(accessor);//通知顶层改变
model.$events&&model.$fire(name,neo,value);
}
}else{
if(openComputedCollect||!accessor.locked){
collectSubscribers(accessor);
}
returnvalue=json[name]=getter.call(model);//保存新值到json[name]
}
};
accessor.nick=name;
callGetters.push(accessor);
}else{
value=NaN;
callSetters.push(name);
accessor=function(neo){//创建监控属性或数组
if(arguments.length){
if(stopRepeatAssign){
return;//阻止重复赋值
}
if(value!==neo){
varold=value;
if(valueType==="Array"||valueType==="Object"){
if(value&&value.$id){
updateViewModel(value,neo,Array.isArray(neo));
}elseif(Array.isArray(neo)){
value=Collection(neo,model,name);
}else{
value=modelFactory(neo);
}
}else{
value=neo;
}
json[name]=value&&value.$id?value.$json:value;
notifySubscribers(accessor);//通知顶层改变
model.$events&&model.$fire(name,value,old);
}
}else{
collectSubscribers(accessor);//收集视图函数
returnvalue;
}
};
}
accessor[subscribers]=[];
Descriptions[name]={
set:accessor,
get:accessor,
enumerable:true
};
}
});
if(defineProperties){
defineProperties(model,Descriptions);
}else{
model=VBDefineProperties(Descriptions,VBPublics);
}
VBPublics.forEach(function(name){
if(!watchOne[name]){
model[name]=scope[name];
}
});
callSetters.forEach(function(prop){
model[prop]=scope[prop];//为空对象赋值
});
callGetters.forEach(function(fn){
Publish[expose]=fn;
callSetters=model[fn.nick];
fn.locked=1;
deletePublish[expose];
});
model.$json=json;
model.$events={};//VB对象的方法里的this并不指向自身,需要使用bind处理一下
model.$watch=Observable.$watch.bind(model);
model.$unwatch=Observable.$unwatch.bind(model);
model.$fire=Observable.$fire.bind(model);
model.$id=generateID();
model.hasOwnProperty=function(name){
returnnameinmodel.$json;
};
returnmodel;
}
VM是用ecma262v5的新API,Object.defineProperties生成的一个充满访问器的对象,这样的对象,能通过用户对它的属性的读写,触发定义时的getter,setter函数。getter,setter对rubyer,pythoner,C#er应该很熟悉,我就不展开了。
[align=left]旧式IE,avalon利用VBScript的类实例,它也存在其他语言的访问器。不过,VBS对象不像JS对象那样随意添加新属性,删除已有属性,因此我们就无法监后添加的新属性。Object.defineProperties也一样,它能处理的属性也只是它定义时的属性,想监控后来的,需要再调用一次Object.defineProperties。[/align]
整个工厂方法内部都是围绕着scope处理
过滤监控的属性
收集视图函数
转换用于定义
skipArray//要忽略监控的属性名列表
0:"$json" 1:"$skipArray" 2:"$watch" 3:"$unwatch" 4:"$fire" 5:"$events"
我们还是已官网的demo为列
avalon.define("simple",function(vm){ vm.firstName="司徒" vm.lastName="正美" vm.fullName={//一个包含set或get的对象会被当成PropertyDescriptor, set:function(val){//set,get里面的this不能改成vm vararray=(val||"").split(""); this.firstName=array[0]||""; this.lastName=array[1]||""; }, get:function(){ returnthis.firstName+""+this.lastName; } } }) avalon.scan(document.querySelector("fieldset"));
此时传入的vm为
$watch:functionnoop(){ firstName:"司徒" fullName:Object lastName:"正美"
意图很明显就是遍历这些属性,给出相对应的处理,具体我们接着往下看
纯净的js对象,所有访问器与viewModel特有的方法属性都去掉
if(!watchOne[name]){
[code]json[name]=value;
}
[/code]
几个简单的条件过滤:
//判断类型
[code]varvalueType=avalon.type(value);
if(valueType==="Function"){
//第一个就是$watch"被重复假如到列表了
VBPublics.push(name);//函数无需要转换
}else{
[/code]
跳过过滤的条件后:
核心的转换
转换计算属性
转化监控属性
转换计算属性:
定义时为一个最多拥有get,set方法的对象(get方法是必需的)注意,get,set里面的this不能改为vm,框架内部会帮你调整好指向。
判断的条件,值类型是对象,并且有get方法,并且方法要少于等于2个
if(valueType==="Object"&&typeofvalue.get==="function"&&Object.keys(value).length<=2){
满足条件的
vm.fullName={//一个包含set或get的对象会被当成PropertyDescriptor,
set:function(val){//set,get里面的this不能改成vm
vararray=(val||"").split("");
this.firstName=array[0]||"";
this.lastName=array[1]||"";
},
get:function(){
returnthis.firstName+""+this.lastName;
}
}
具体有什么用我们接着往下看
转化监控属性
定义时为一个简单的数据类型,如undefined,string,number,boolean。监控数组:定义时为一个数组
firstName:"司徒"
accessor[subscribers]=[];
别看这个代码是空的函数,不起眼,双向绑定就是看他了,我们先Mark下
//生成defineProperties需要的配置属性
Descriptions[name]={
set:accessor,
get:accessor,
enumerable:true
};
Descriptions临时对象//收集内部用于转换的对象
enumerable很重要,为false的话,forin就找不到它了
这样循环后就把该干嘛的不该干嘛的都给区分开了
最后都保存在Descriptions中
此时的Descriptions
Descriptions:Object
[code]
firstName:Object
enumerable:true
get:function(neo){//创建监控属性或数组
set:function(neo){//创建监控属性或数组
fullName:Object
enumerable:true
get:function(neo){//创建计算属性
set:function(neo){//创建计算属性
lastName:Object
enumerable:true
get:function(neo){//创建监控属性或数组
set:function(neo){//创建监控属性或数组
[/code]
看吧就是这样给包装了一下,只是定义了但是还没生效
所以defineProperties(model,Descriptions);给执行以下(defineProperties的方法见前面)
model就是工厂模式转换后的新的vm模型对象了,因为在开始遍历scope的过滤了一些东东,原本也是用户定义的,所以这时候我们还得加到新的vm-》model中去、
//添加用户定义的未转换的函数到模型
VBPublics.forEach(function(name){
if(!watchOne[name]){
model[name]=scope[name];
}
});
相关文章推荐
- 轻量级前端MVVM框架avalon - 模型转换
- 轻量级前端MVVM框架avalon源码分析-总结
- 轻量级前端MVVM框架avalon - 整体架构
- 轻量级前端MVVM框架avalon - 初步接触
- 轻量级前端MVVM框架avalon - 整体架构
- 轻量级前端MVVM框架avalon - ViewModel
- 前端MVVM框架avalon揭秘
- 轻量级前端MVVM框架avalon - 执行流程1
- 前端MVVM框架avalon学习笔记
- 轻量级前端MVVM框架avalon - 整体架构
- 轻量级前端MVVM框架avalon:整体架构
- 前端MVVM框架avalon揭秘 - HTML编译器
- 轻量级前端MVVM框架avalon - 控制器
- 轻量级前端MVVM框架avalon源码分析
- 前端MVVM框架avalon揭秘 - 双向绑定原理
- 轻量级前端MVVM框架avalon - 整体架构
- 前端MVVM框架avalon揭秘 - HTML编译器
- avalon 开源的一个迷你、易用、高性能的前端 MVVM 框架
- 轻量级前端MVVM框架avalon - ViewModel
- 轻量级前端MVVM框架avalon