随笔-57  评论-129  文章-0  trackbacks-0
  2007年6月25日
Java的成功,离不开它那个庞大的类库,不单是sun的类库,很多细节的实现都取自第三方(如xml解析采用Apache的实现)。

JSI暂时不大算编写丰富的公共API,但是我们可以集成其他成熟的类库,同时隔离他们的依赖,隔离各个脚本的执行上下文,消除命名冲突的危险。

这里我们详细介绍一个复杂一点的实例:类似Windows XP文件浏览器左侧的滑动折叠面板(任务菜单)效果。

我们先集成Scriptaculous Effect类库,并且在这个基础上按我个人的习惯对一个面板折叠效果做一个简单的封装,展示框架的类库封装功能。

1。集成Scriptaculous类库:

这里我们不做过多介绍,详细情况请参考集成实战;我们发布的版本中已经把Scriptaculous放置于us.aculo.script包中,您可以把这些作为系统内置的类库使用。

2。编写我们的折叠面板函数(example/effect.js):
/*
 * 滑动面板实现. 
 * 当指定元素可见时,将其第一个子元素向上滑动至完全被遮掩(折叠)。 
 * 当指定元素不可见时,将其第一个子元素向下滑动至完全显示(展开)。 
 
*/  
function slidePanel(panel){  
  panel 
= $(panel);  
  
if(panel.style.display=='none'){  
    
//调用Scriptaculous Effect的具体滑动展开实现  
    new Effect.SlideDown(panel);  
  }
else{  
    
//调用Scriptaculous Effect的具体滑动闭合实现  
    new Effect.SlideUp(panel);  
  }  
}  

3。编写包定义脚本(example/__$package.js):
//添加slidePanel(滑动面板控制)函数  
this.addScript("effect.js","slidePanel",null);  
//给effect.js脚本添加对us.aculo.script包中effects.js脚本的装载后依赖this.addScriptDependence("effect.js",  
"us/aculo/script/effects.js",false);

4。在网页上运用上面的类库:
<html>  
  
<head>   
  
<title>重用aculo Effect脚本实例</title>  
  
<link rel="stylesheet" type="text/css" href="/styles/default.css" />  
  
<script src="/scripts/boot.js"></script>  
  
<script>  
    $import(
"example.slidePanel");  
  
</script>  
  
</head>  
  
<body>  
    
<div class="menu_header"  
        onclick
="slidePanel('menu_block1')">  
        面板 1  
    
</div>  
    
<div class="menu_block" id="menu_block1">  
      
<ul>  
        
<li>text1</li>  
        
<li>text2</li>  
        
<li>text3</li>  
      
</ul>  
    
</div>  
</body>  
</html> 

onclick="slidePanel('menu_block1')"这个事件函数将在我们点击面板标题时触发,能后会调用Scriptaculous Effect的具体实现去实现我们需要的滑动折叠功能。


 

壁立千仞 无欲则刚――控制依赖


java可以随意的使用第三方类库,可是JavaScript却没那么幸运,随着类库的丰富,烦杂的依赖关系和可能的命名冲突将使得类库的发展越来越困难。程序的易用性也将大打折扣。

命名冲突的危险无形的增加你大脑的负担;随着使用的类库的增加,暴露的依赖也将随之增加,这是复杂度陡增的极大祸根,将使得系统越来越复杂,越来越难以控制。潜在的问题越来越多,防不胜防。

JSI的出现,可以解决上述问题,我们建议类库的开发者将自己类库的依赖终结在自己手中,避免依赖扩散,以提高类库的易用性。

同样使用上面的例子,假如我们想抛开JSI,实现同样的功能,那我们的页面代码将是(类库代码不用改动):

<html>  
  
<head>   
  
<title>重用aculo Effect脚本实例</title>  
  
<link rel="stylesheet" type="text/css" href="/styles/default.css" />  
  
<!--  
  <script src="/scripts/boot.js"></s cript>  
  <script>  
    $import("example.slidePanel");  
  </sc ript>  
   
-->  
<script src="/scripts/net/conio/prototype/v1_5/prototype.js">  
</script>  
<script src="/scripts/us/aculo/script/v1_7/effects.js">  
</script>  
<script src="/scripts/us/aculo/script/v1_7/builder.js">  
</script>  
<script src="/scripts/example/effect.js">  
</script>  
  
</head>  
  
<body>  
    
<div class="menu_header"  
        onclick
="slidePanel('menu_block1')">  
        面板 1  
    
</div>  
    
<div class="menu_block" id="menu_block1">  
      
<ul>  
        
<li>text1</li>  
        
<li>text2</li>  
        
<li>text3</li>  
      
</ul>  
    
</div>  
</body>  
</html>  

这个例子的html代码明显比上面的复杂了,一堆堆的script标签,而且还是有序的;还出现在页面上,重构起来也极其麻烦。

可以看出,JSI的加入可以让类库更加易用,html代码更为简洁,最终用户已经不必关心所用类库的依赖了。

JSI中每一个脚本有一个单独的执行上下文。各个脚本顶部变量你可以随便使用,不必担心不同脚本中的命名冲突,不会污染全局变量空间,这种方式可以用于解 决某些类库间变量冲突的问题(如jQuery和Prototype的$函数)。我们甚至可以做到同一个页面上间接加载同一种类库的两个不同版本,不相互影 响。

使用JSI后,很多细节我们可以在包中封装掉,不需要告诉类库使用者太多。大大增加类库的易用性。同时,类库封装的支持可以让我们在第三方库的基础上轻松的按自己的喜好编写自己的类库,同时避免依赖扩散造成的复杂度增加。

使用JSIntegration唯一多出的负担就是编写包定义文件,不过想想这种定义文件可是一劳永逸的(以后就不需要每次导入脚本的时候都小心翼翼的判 断那个脚本先导入那个后导入,有那些间接使用到的类库需要导入,等等),而且有了包结构后对于代码组织、重用,以及文档的编写阅读,都将非常有利。
posted @ 2007-06-25 10:14 金大为 阅读(586) | 评论 (0)编辑 收藏

何谓安需装载?

脚本程序一般都是下载后执行 ,当脚本库非常庞大时,一次性下载起来非常费时,传统的解决方式是,按功能模块把脚本写在不同的文件中,页面上手动加入script标签装载指定内容,但 是这有一些缺点,类库的使用者需要知道没个脚本之间的关系,顺序要求等等,而不可能要求每个类库使用者都对其非常熟悉,出错的可能性很大。于是很多框架开 始支持导入指令,想使用什么一个导入函数就完了,不必一堆堆的script文件,不用小心翼翼的关注着他们的依赖关系。

安需装载可分如下三种模式:

  • 即时同步按需装载阻塞,JSI、JSVM、dojo)
    最简单的按需装载实现,通过XMLHttpRequest同步装载脚本文件实现。问题是,浏览器使用这种方式同步获取资源时将导致浏览器阻塞:停止响应用户事件、停止页面重画操作。所以,虽然编程最为简单,但是用户体验最差。
  • 异步装载无阻塞,JSI2.0+)。
    异步导入,不必多做解释,用户体验好,但是因为其异步特征,处理起来比较麻烦。
  • 延迟同步按需装载无阻塞,JSI2.0+)
    JSI通过动态预装载功能实现的一种同步获取资源的方法,虽然也是同步,但没有阻塞,可以算时兼顾易用性和用户体验的机决方按。缺点时
    有一定延迟,当前脚本标签中不可用。

使用方法(JSI示例)

以一个代码语法着色程序为例:
类库位置:example/codedecorator/code.js
页面位置:example/xxx.html
  • 即时同步按需装载
    $import("example.codedecorator.Code");

    var code1 = new Code();
    code1.id 
    = "libCode";
    code1.decorate(); 

  • 异步装载
    $import("example.codedecorator.Code",function(Code){
     
    var code1 = new Code();
     code1.id 
    = "libCode";
     code1.decorate();
    }) 

  • 延迟同步按需装载无阻塞,JSI2.0+)
    <script>"../scripts/boot.js"></script>
    <script>
    $import(
    "example.codedecorator.Code",true);
    </script>

    <script>
    var code1 = new Code();
    code1.id 
    = "libCode";
    code1.decorate();
    </script>

示例说明:

在线测试

http://jsintegration.sourceforge.net/example/code.html

http://www.xidea.org/project/jsi/example/code.html

参考:

JSI 导入函数: function $import(path, callbackOrLazyLoad, target )

posted @ 2007-06-25 09:24 金大为 阅读(551) | 评论 (0)编辑 收藏

这个问题算是个老问题了。
所以,标题加上一个再字。

我主要说一下模拟这个事件时候容易出现的问题。

对于FF,Opera9,原生DOMContentLoad支持,就不说了,最头疼的是IE。
我最初考虑这个问题的时候,想到的是document的readyState属性。
偷窥一下Dojo的源码,发现它也是基于这个属性去做的,那时,我基本就认准了这个方法。
于是再后来JSI1和JSI2早期的预览版本中,都是基于这个属性做的,后来,有个朋友向我反应,说JSI的装饰引擎在IE上启动比FF慢很多,我当时就猜测,是否是这个DOMContentLoad的问题。

经过测试,发现,确实,readyState必须等待图片装载完成之后才能置为complete。
于是到网上搜索一下其他办法,最终 Dean Edwards的blog上描述的一个基于script defer属性的解决办法胜出:http://dean.edwards.name/weblog/category/dom/onload/
不过,使用document.write打印一段脚本,我真的不喜欢:(

在评论里面我们可以看到一些其他的建议,有人认为 readyState == 'complete' 加 readyState==‘interactive’这个状态可以准确模拟DOMContentLoad。我开始简单测试一下,貌似可行,但是后来发现 readyState==‘interactive’可能受alert等函数影响,就是说,一般情况下,readyState== ‘interactive’时,dom是装载完了的,但是,当你文档装载过程中,调用了alert函数,那么,readyState将提前置为 ‘interactive’。

总之,目前来说,我知道的,只有两个办法可以真正实现IE上模拟DOMContentLoad

    1.htc ondocumentready(需要额外的HTC文件)
    2.script defer(必须使用document.write打印一段脚本标记,jquery目前采用的方式)

最常见的一个错误:
    document.readyState(Dojo目前(0.4.1)采用的方式)

再说一下JSI的DOMContentLoad实现的使用接口。

js.html.EventUtil.addDOMReadyListener(<Function>listener ,<boolean>runAnyCase )

第二个参数用于确保listener 在任何时候都会被调用(FF DOMContentLoad事件如果在事件发生之后设置listener是无效的)

posted @ 2007-06-25 09:13 金大为 阅读(531) | 评论 (0)编辑 收藏