JavaScript中的View-Model使用介绍
JavaScript 使用 介绍 View model
2023-06-13 09:14:30 时间
构成
这是一个十分常见的微博列表页面,类似于新浪微博。上周末,在心无旁骛情况下,一共用了5个对象,产出400行代码,实践出一种代码组织模式。
使任务轻松完成的代码有4个方面的要素组成:
要素
组成
模型
Reply、Forward
视图
CommentEditor、ReplyList、ForwardList
模板
jQuery.tmpl
异步任务
jQuery.Deferred 分部介绍
模型
模型只与数据有关,它能够产生、过滤、保存、验证数据,并且仅此而已。
如下例,留言模型在调用保存方法时,只接收JSON参数,并且只返回一个异步任务,实际处理时同步或异步的返回结果并不重要。
在此进行的验证的原因是,它是一个开放的对象,是与服务器交互的最后一道门槛。
另外,它本身也不处理验证失败的情况——由视图调用时选择性地处理,可能会弹出一个消息提示或直接忽略再进行重试。
复制代码代码如下:
这是一个十分常见的微博列表页面,类似于新浪微博。上周末,在心无旁骛情况下,一共用了5个对象,产出400行代码,实践出一种代码组织模式。
使任务轻松完成的代码有4个方面的要素组成:
模型
模型只与数据有关,它能够产生、过滤、保存、验证数据,并且仅此而已。
如下例,留言模型在调用保存方法时,只接收JSON参数,并且只返回一个异步任务,实际处理时同步或异步的返回结果并不重要。
在此进行的验证的原因是,它是一个开放的对象,是与服务器交互的最后一道门槛。
另外,它本身也不处理验证失败的情况——由视图调用时选择性地处理,可能会弹出一个消息提示或直接忽略再进行重试。
//留言模型
varReply={
cache:{},
//{sourceid:id,page_size:10,page_num:1}
fetch:function(data){
return$.post("/ajax/blog/reply/list",data||{}).success(function(resp){
resp.ok&&resp.list&&
$.each(resp.list,function(k,v){
returnReply.cache[v.id]=v;
});
});
},
//filter("name","king")
filter:function(prop,val){
return$.grep(this.cache,function(r){returnr[prop]===val});
},
//{content:"想说就说",sourceid:1001}
create:function(data){
//promise
vardfd=$.Deferred(),now=$.now();
if((now-this.create.timestamp)/1000<10){
dfd.reject({message:"您发表得太快了,休息一下吧",type:"warn"})
}elseif(!data||!data.sourceid){
dfd.reject({message:"非法操作",type:"error"})
}elseif(!data.content){
dfd.reject({message:"评论内容不能为空",type:"warn"})
}else{
this.create.timestamp=now;
dfd=$.post("/ajax/blog/reply/create",data);
}
returndfd.promise();
}
};
Reply.create.timestamp=Forward.create.timestamp=$.now()-1e4;
视图
视图是浏览器页面上的可视部分,每个视图对象含有一个关联的jQuery对象作为属性(instance.$el),类似于UI组件中的DOM容器。
视图还有两个一致的方法:
render方法用于从模型获取数据,并且根据定义好的模板将数据渲染到HTML页面上。
activate方法用于激活视图,同时绑定相关的DOM事件,所有事件至多委托到$el为止。
这个示例中,CommentEditor是父视图,ReplyList和ForwardList是互斥显示的两个子视图,父子视图之间相互保存引用。
//回复列表视图
varReplyList=function(options){
varopt=this.opt=$.extend({
el:"",
parent:null
},options||{});
this.parent=opt.parent;
this.$el=$(opt.el);
this.activate();
};
ReplyList.prototype={
render:function(){
varself=this;
Reply.fetch({
page_size:10,page_num:1,
sourceid:self.parent.getBlogId()
})
.done(function(data){
self.$el.html(self.$list=$.tmpl(tpl_reply_list,data));
});
returnself;
},
activate:function(){
this.$el.delegate("a.del",$.proxy(this.del,this))
}
//...
}
//评论编辑器视图
CommentEditor.prototype={
activate:function(){
this.$el.delegate("a.save",$.proxy(this.save,this))
},
save:function(){
varself=this,data={content:self.getContent(),sourceid:self.getBlogId()};
vartask_r=Reply.create(data);
vartask_f=Forward.create(data);
//转发、评论同时进行
$.when(task_r,task_f).then(function(t1,t2){
//保存成功,更新视图或关闭
},function(data){
//模型验证出错,或远程服务器错误
Sys.info(data.message,data.type);
});
returnself;
},
switchView:function(type){
//切换子视图
varview_opt={el:this.$sublist.empty(),parent:this};
if(type==="reply"){
$label.show();
this.$submit.val("评论");
this.sublist=newReplyList(view_opt).render();
}else{
$label.hide();
this.$submit.val("转发");
this.sublist=newForwardList(view_opt).render();
}
}
//...
}
模板
模板可以消除繁琐、丑陋的字符串拼接,它的作用是能够直接由js对象生成HTML片断。
模板中可以直接遍历对象,套用预定义的函数,来对一些数据进行格式化,比如时间函数nicetime:
//回复列表模板
vartpl_reply_list="<ulclass="ui-reply-list">\
{{eachlist}}\
<lidata-id="${id}">\
<aclass="name"href="/${userid}">${name}:</a>\
<p>${content}</p>\
<timepubdate>${nicetime(timestamp)}</time><aclass="del"href="javascript:;">删除</a>\
</li>\
{{/each}}\
</ul>";
异步任务
DeferredObject的直译是延迟对象,但是理解成异步任务更为恰当。异步任务能够消除多层嵌套的回调,让代码书写和阅读更为便利。
从上面的模型和视图的代码中可以明显地看出,使用了异步任务之后,代码变得更加平面化了。
$.Deferred方法新建的是一个双向任务队列:成功回调函数队列和失败回调函数队列;任务的状态也分为两种:成功和失败,分别可以用isResolved或isRejected来检查任务的当前状态、用resolve或reject修改任务状态。
promise方法返回任务的只读副本,此副本上不能修改任务状态。毫无疑问,模型应该始终只返回promise对象。(注:只读副本仍然可以再次调用promise方法再次返回只读副本)
在Reply.create方法中,能够更好地处理自定义的异步任务,而不是直接返回原生的ajax异步任务:
//vardfd=$.Deferred();
$.post("/ajax/blog/reply/create",data)
.success(function(json){
if(json&&json.ok){
dfd.resolve(json.list);
}else{
dfd.reject({message:json.message||"获取失败",type:"error"});
}
})
.fail(function(){
dfd.reject({message:"服务暂时不可用",type:"error"})
});
目的及结论
为什么拆散成这样?
收获:可维护性,清晰的API调用、消除二层以上的if语句、消除二层以上的回调语句、每个函数控制在二十行之内。
结果:没有过多的重复代码,所有的功能都被打包好了。
相关文章
- 【说站】javascript加法如何使用
- JavaScript 获取浏览器 Flash 版本
- js 怎么使用正则表达式-理解Javascript的正则表达式
- 使用JavaScript写的一个旋转的彩圈详解编程语言
- JavaScript学习总结(一)——JavaScript基础详解编程语言
- Javascript实例教程(19)使用HoTMetal(2)
- 如何在WebForm中使用javascript防止连打(双击)
- javascript(js)join函数使用方法介绍
- Javascript中的window.event.keyCode使用介绍
- 关于JavaScript的with语句的使用方法
- Javascript中使用exec进行正则表达式全局匹配时的注意事项
- javascript之querySelector和querySelectorAll使用介绍
- Javascript引用指针使用介绍
- JavaScript调用堆栈及setTimeout使用方法深入剖析
- 使用JavaScript实现各种跨域的方法
- 使用indexOf等在JavaScript的数组中进行元素查找和替换
- javascript中的document.open()方法使用介绍
- javascript实用小函数使用介绍
- javascript中的if语句使用介绍
- javascript中的作用域和上下文使用简要概述
- javascript中数组的冒泡排序使用示例
- javascript类型转换使用方法
- javascript对象的使用和属性操作示例详解
- JavaScript中使用ActiveXObject操作本地文件夹的方法
- JavaScript中的Math使用介绍
- JavaScript使用循环和分割来替换和删除元素实例
- JavaScript中使用Object.create()创建对象介绍
- JavaScript中document对象使用详解
- 深入理解Javascript中this的作用域