您的位置:首页 > Web前端 > JavaScript

【javascript】最简洁的TreeView、“蛮力跨域”、Jsonp协议、局部运算以及仿客户端的Web视频应用例子(含下载)

2009-07-23 21:32 676 查看
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

简单经验小结,性烈老鸟莫入!偶尔眼不见、心不烦啊!

最近弄了个Web应用:有大量的数据需要以树形结构展示,另外数据存储在不同域名的站点。

首先要解决展示的问题。我刚开始引用了jquery插件simpleTreeD&D,易用、好用,推荐给大家;引用个小例子:

<li><spanclass="text">TreeNode1-1</span>
<ulclass="ajax">
<li>{url:tree_load.php?tree_id=1}</li>
</ul>
</li>

$('#SomeTreeId').simpleTree({
afterMove:function(dd,ds,pos)
{
varmsg="position-"+pos+"\n"
+"dragdestinationID-"+$(dd).attr("id")+"\n"
+"dragsourceID-"+$(ds).attr("id");
alert(msg);
}
});


只可惜我引用的json实例数据量很大(单文件最大2m左右),下载就颇费时间,再用这个树形控件计算、绘图:持续相当一段时间的无响应状态!

网上搜了个极其简单的、没使用任何框架的实现树形视图的例子,遗憾的是现在回头怎么也找不到了,实在不知道当初自己用的什么搜索关键字。好在源码大意还记得,能说明一些问题:

<scripttype="text/javascript">
window.nodeClick=function(sender){
sender.title=sender.title=='none'?'':'none';
varv=sender.nextSibling;
while(v.nodeName!='B'){
v.style.display=sender.title;
v=v.nextSibling;
}
}
</script>
<bonclick="nodeClick(this)">a</b><br/><a>1</a><br/><a>2</a><br/><a>3</a><b></b><br/>
<bonclick="nodeClick(this)">b</b><br/><a>1</a><br/><a>2</a><br/><a>3</a><b></b><br/>
<bonclick="nodeClick(this)">c</b><br/><a>1</a><br/><a>2</a><br/><a>3</a><b></b><br/>


受此启发,在后来多次的实验中,我最终确定了如下的HTML格式:

<ul>
<li><b>+</b><a>A</a>
<ul>
<li><b>+</b><aonclick="">a</a>
<ul>
<li>1</li>
<li>2</li>
</ul>
</li>
<li><b>+</b><aonclick="">a2</a>
<ul>
<li>1</li>
<li>2</li>
</ul>
</li>
</ul>
</li><li><b>+</b><a>B</a>
<ul>
<li><b>+</b><aonclick="">b</a>
<ul>
<li>1</li>
<li>2</li>
</ul>
</li>
<li><b>+</b><aonclick="">b2</a>
<ul>
<li>1</li>
<li>2</li>
</ul>
</li>
</ul>
</li>
</ul>

window.qv_OnNodeToggle=function(sender){//触发节点展示切换
varv=$(sender.parentNode);
varul=v.children("ul");
varb=v.children("b");
v=b.html();
switch(b.html()){
case('+'):
b.html('-');
ul.show();
break;
case('-'):
b.html('+');
ul.hide();
break;
default:
alert('正在加载当前分类,请稍等...');
break;
}
event.cancelBubble=true;
};

这就是“最简洁的TreeView”!该结构简洁明了,比较容易维护,而且只有必要数据(消减了图标、连线等),对于要求性能第一的应用来说,再合适不过。

接下来是脚本跨域的问题。起手时,我运用了一个非常蛮力的方法:
window.LoadOnlineCategory=function(categoryId,categoryName){
window.w=window.open("","newwin","height=200px,...,menubar=no");
varhtml=newArray();
html.push('<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0....\r\n');
html.push('<htmlxmlns="http://www.w3.org/1999/xhtml">\r\n');
html.push('<head>\r\n');
html.push('<title>');
html.push(categoryName);
html.push('-正在加载节目列表...</title>\r\n');
html.push('</head>\r\n');
html.push('<bodystyle="background-color:Black;color:White">\r\n');
html.push('<bstyle="color:Red">请勿关闭本窗口</b>!...自动关闭</b>...\r\n');
html.push(['<script...src="http://.../Category_',categoryId,'.js"><\/script>\r\n'].join(''));
html.push('<scripttype="text/javascript">\r\n');
html.push('window.timer=1;\r\n');
html.push('window.done=function(){\r\n');
html.push('if(window.queenVideos!=undefined&&...){\r\n');
html.push('window.opener.CategoryLoadedCallback(window,window.queenVideos[0]);\r\n');
html.push('}\r\n');
html.push('else{\r\n');
html.push('document.title=["(",window.timer++,")正在加载节目列表..."].join("");\r\n');
html.push('setTimeout(window.done,1000);\r\n');
html.push('}\r\n');
html.push('};\r\n');
html.push('window.onload=function(){\r\n');
html.push('setTimeout(window.done,3000);\r\n');
html.push('};\r\n');
html.push('if(window.queenVideos==undefined){\r\n');
html.push('document.location.reload();\r\n');
html.push('}\r\n');
html.push('<\/script>\r\n');
html.push('</body>\r\n');
html.push('</html>');
w.document.write(html.join(''));
};

window.CategoryLoadedCallback=function(subWindow,category){
vars=document.getElementById(['ul_',category.c].join(''));
varhtml=newArray();
varseries=category.v;
varfiles;
for(vari=0;i<series.length;i++){
//...
}
subWindow.close();
s.innerHTML+=html.join('');
};

在IE8下,居然能如预期地运行;两天前,使用这种方式加载跨域数据的应用发布了。这就是我所谓的“蛮力跨域”,只是其似乎不支持Chrome和Firefox,让我觉得遗憾。

我给一位负责脚本开发的前同事看,被一顿鄙视,他跟我提了下Jsonp协议;在大致弄清了我的“野蛮”做法后,并不忘最后来了句:“SB和NB仅一步之差!”我不以为然,了解了下Jsonp协议,发现涉及到数据提供方的接口形式约束(必须以callbackFunc({...})的形式返回数据),更是觉得实用性差。

我给一位广州的小鸟看(他的技术无论dota还是编程确实都很菜!),这鸟表现的饶有兴趣,我观察他确实有心去用,因此对他的意见多给了点关注;但他提了很多毫不客气的批评牢骚,打着“完全站在客户角度”的旗帜,其实就是技术上小白。终了连web播放器和客户端播放器都没整清爽!

他说这个鸟东西弹窗口真烦人!还要装什么鸟加速器,更烦人!

老子试图探讨的海量数据的加载、展示技术细节,它全然不懂、不管!

我一咬牙,还是用Ajax体验要好很多,还是试试Jsonp吧。

因为数据量很大,所以要求必须减少、甚至不用框架,不然用户会感觉页面加载很慢!我翻出了我早前包装的一段Ajax包装代码:

window._AjaxBuilder=function(){
if(window.ActiveXObject){
varMSXML=newArray('MSXML2.XMLHTTP','Microsoft.XMLHTTP',....);//IE5,6,7
for(vari=0;i<MSXML.length;i++){
try{
varrequestor=newActiveXObject(MSXML[i]);
Ajax.prototype._GetRequestor=function(){
returnnewActiveXObject(MSXML[i]);
}
break;
}
catch(e){
}
}
}
elseif(window.XMLHttpRequest){//forFirefox,Opera,IE7
Ajax.prototype._GetRequestor=function(){
returnnewXMLHttpRequest();
}
}
else{
throw{name:"ExplorerException",message:"不可识别的浏览器,创建_AjaxBuilder失败."};
}
};
window.Ajax=function(){
if(this._ajaxBuilder==null)
this._ajaxBuilder=new_AjaxBuilder();
varrequestor=this._GetRequestor();
var_callbackId=null,_requestType='GET',_isAsynch=true,...;
function_Callback(){
//...
}
this._SetArgs=function(callbackId,DoneCallback,...,isAsynch){
//...
}
this._Send=function(url,datas){
//...
}
};
window.Ajax.prototype={
_ajaxBuilder:null,
_GetRequestor:null,
Send:function(url,callbackId,DoneCallback,ExceptionCallback,...,datas){
this._SetArgs(callbackId,DoneCallback,...,isAsynch);
this._Send(url,datas);
}
};
window.ajax=newAjax();

我将返回数据改造成如下:

qv_LoadCategories([{i:15,n:'动作片'},{i:16,n:'喜剧片'},{i:13,n:'综艺娱乐'}]);

本地测试时非常正常(直接打开电脑的一个htm文档d:/index.html,文档内请求files.cnblogs.com上的jsonp协议数据)。我满怀希望的将脚本插入我的博客(http://www.cnblogs.com/),结果我的Ajax在请求数据时返回错误码0!

实在没时间也懒得再去调试这些底层问题,还是请出jquery大神吧。看到它最小大概50k左右,反复权衡,决定自己还是从了算了!

$.ajax({
url:[window.qv_server,'qv.js'].join(''),
dataType:"jsonp"
});

寥寥几行,解决了我所有的问题!后来的一两天时间里,我跟着把加载的过程、播放的过程变得更加简便、友好,以及增加类似电视剧剧集的关联播出等。

这就是jsonp,当运用后才发现,原来脚本跨域可以变得这么优雅、简单!

我把成果赶紧展示给那位脚本同事,最后还不忘加了句:“都SB了,NB还会远吗?”

运用Ajax技术,是为了局部刷新,为了提高用户的体验。我在处理大量数据的发觉,在运算的过程中也不妨Ajax一下。

当我点击“大陆剧”的时候,意味着要加载接近一万部视频文件的相关信息(名称、播放串等),文件本身就接近2m;浏览器在解析该庞大json数据时甚至还会卡一段时间。那么在我创建树形结构的时候,基本就是无响应状态了,时常浏览器还会显示超时错误,要强行中断脚本执行等;好在我使用了最简洁的TreeView结构,否则这个问题更加严重!

这些视频数据是增量的,据估测,最终这些数据将扩大至现在的4倍多,那么我当前的运算模式肯定是不适应的了!我猛然间有了上文提到的想法(在进行Ado/.Net数据库操作时,我有过类似做法:为了防止某个连接超时,就在单次连接期间做小的一个分片的活,通过循环多次完成整个任务):

window.qv_LoadCategory=function(category){//加载某个分类节目
//...
setTimeout(window.qv_LoadCategoryPart,100);
};
window.qv_LoadCategoryPart=function(){//局部加载分类节目
varcount=0;
do{
if(count>100){
//...
setTimeout(window.qv_LoadCategoryPart,1000);
return;
}
series=window._categoryTemp.v.pop();
//....
}while(window._categoryTemp.v.length>0);
//...
$('#qv_listStatus').html('    节目列表(点击节目播放)');
};

事实上,我更愿意把这理解成javascript独特的多线程编程模式。即使运用setTimeout去运行某个大工作量函数,浏览器照样会明显反应变慢。因此只有做一小段活,然后休息一会(延时1秒执行),这样浏览器的执行时间就被你和你的程序“分时”共享了,并且各自都还显得顺畅,即便程序执行的总时间变长了。我在应用过程中发现,用户其实不需要很快加载完所有数据,而是要能尽快使用上已加载的数据(尽快的响应)!我的这个分片、多次运算的方法还算比较好的满足了这个需求!

这段日子在持续地看一本说.net框架的书,每天读一点。初级的程序员要求的不过是“实现功能”,而稍专业些的要多花些精力在性能、扩展性等方面。没有引用jquery前,我所有的HTML标签创建都是靠的拼接字符串,引用后,我全用了它了构造器,我相信框架一定是以最优的方法去实现的;这也是框架的价值所在。

本文虽然只是记录了几个实践环节中的小的点,但却是有实用价值的:有强烈性能要求的场合,不妨构造自己的轮子(TreeView);在实在没有服务端支持的前提下,不妨使用“蛮力”哪怕丑陋的解决一个问题(蛮力跨域);当然,最好的办法是得到服务端支持,一起优雅的解决困难(jsonp协议);在庞大的工作量面前,学会分而治之,劳逸结合(局部运算);最后,至少我现在不用下载客户端(安装后经常有隐秘进程干扰),却能在web上像在客户端上一样看看高清电影、电视了吧(解决海量数据加载、分析的策略问题;同时邀请大家试用)。

补充:我在默认的文章签名里插入了该Web视频应用的代码;因博客园默认引用了Jquery库,而我的初始界面创建代码只有几百字节,因此不会对大家的页面加载速度造成任何影响(它只在当前页面其他部分全部加载完成之后才异步创建)。希望有兴趣的网友在试用的过程中多提意见,尤其可指导一下如何使得应用数据加载、展示更优。至于视频内容上的需求,也可联系我,我可以不定期更新博客园视频数据的快照(之所以不公开源,是为了防止恶意的网络攻击行为)。

本地播放页面下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: