锁定老帖子 主题:Ext 2.0 for JSVM
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-11-06
wch3116 写道 1. 严格按照一个“类/对象”对应一个相同名称的文件的方式保存。 基本上所有的库都用这种方式来管理源代码 |
|
返回顶楼 | |
发表时间:2007-11-06
sp42 写道 wch3116 , 取“别名”的一部份原因是向后兼容。
这一点我注意到了,但实际上 Ext 并没有做到很好的向下兼容。 |
|
返回顶楼 | |
发表时间:2007-11-06
zhourenjian 写道 wch3116 写道 1. 严格按照一个“类/对象”对应一个相同名称的文件的方式保存。 基本上所有的库都用这种方式来管理源代码 这个应当被成为规范被严格遵循。Ext 的 source code 用这种方式组织并不困难,但它没有太在意。 |
|
返回顶楼 | |
发表时间:2007-11-06
wch3116 写道 sp42 写道 wch3116 , 取“别名”的一部份原因是向后兼容。
这一点我注意到了,但实际上 Ext 并没有做到很好的向下兼容。 例如? |
|
返回顶楼 | |
发表时间:2007-11-06
zhourenjian 写道 “因为classloader发现已存在A”这种说法,已经带有一定的破除回环算法在里面,不过AB两个类相对来说简单,多几个类九不能简单地说“发现”了。在B尚未被import完毕,A可能是不能被表示为已经存在的。 $import 函数不需要任何返回,仅仅是通知加载,也不能保证加载完成,那么发现回环则可以中断返回。 不过接下来的程序逻辑就要小心了。所以,这种不严格的设计会带来一些意外的复杂性。 |
|
返回顶楼 | |
发表时间:2007-11-08
wch3116 写道 解析Ext模块之间依赖关系的工具非常简陋,几乎没有共享出来的价值,呵呵。
大概原理是:通过正则表达式匹配出当前模块的代码中引用了哪些其它 Ext 模块的名称,然后在文件头部生成 $import 语句。 但是存在一些地方是需要手工调整。因为模块之间文件头部“静态”import 不能形成回环,循环import会造成 jsvm engine 的“死锁”。所以必须将一些造成回环的依赖链打断,调整为运行时动态的import。 启用过smartloader的页面,不会存在多次xhr交互的情况。当然这并不否定import支持多个动态参数是一个好的办法。 此方法确实较为丑陋,只能临时用用,关键仍是模块划分的问题。 关于import回环问题,实际上我觉得应该这样来看待: 如果我们区分namespace(package)和module(unit)的话(新的PIES准备这样设计),则import进来的只是namespace,而不是实际的module(加载模块或加载单元)。namespace理论上绝对应该允许双向依赖,但是module加载是不可能允许双向依赖的。(jindw的jsi,区分了运行时依赖和加载时依赖,使得可以存在某种形式的双向依赖,但这只是js的动态特性,并不改变我们的结论。) 如果存在回环的import,实际上说明,这两个部分应该属于同一个加载单元。(因为它们是互相依赖的,总是必须被一起加载。) 区分namespace和module,稍稍增加了复杂性,但是得到的好处也很多。例如可以允许同时使用一个库的不同版本(它们的namespace还是一个)。 |
|
返回顶楼 | |
发表时间:2007-11-08
wch3116 写道 再谈 js 代码的加载问题
我们在开发一个 rich javascript 应用时,一般会遇到两件事情: 1. 代码应该按功能、模块或其它合理粒度方式切分到多个文件单元中,以便于管理维护和重用。 2. 系统运行时,应该按需加载所要执行的模块代码。因为将程序所有可能要用到的模块都事先全部加载进来听起来并不是一个好主意。 由于 js 代码因为设计上的需要被切分成多个文件,XMLHttpRequest 同步操作造成浏览器阻塞,如果网络不畅,这个过程时间长的话,浏览器的现象是假死(没有响应了) 传统的 <script> 标签方式加载多个 js 文件显然没有出现上面的问题,于是异步加载方案又被重新考虑进来。 其实在比较早(大概是2004年)的一次讨论中,有位网友提到了类似的问题,他提出了一个“异步加载”的方案。我们对此也做了不短时间的讨论。 但我一直坚持以为解决问题的方向不应该是“同步”和“异步”之间的选择问题。 “异步” 不仅打乱了开发人员的一般习惯思维方式,同时也改变了程序顺序加载执行最朴素的规律。 对于模块之间的静态依赖,或者程序对模块的静态需求,采用预加载的方式,然后通过事件侦听或者回调程序入口来继续加载后的的程序执行尚且可以。 但这些依赖或者需求是程序运行时动态决定的,而采用异步的方式肯定造成程序分支增多,增加了系统的复杂性。 而这一切就是为了解决浏览器同步加载多个文件时而造成的“假死”? 浏览器假死不是“同步”这个模式(概念)本身的错误。而是现有的环境(条件)造成的。 试想,如果仍然是同步阻塞,但没有造成浏览器假死是不是也能接受? 所以,我觉得可以通过其它方式来解决这个问题。 本地浏览模式或者网络相当顺畅的情况下,我们几乎不用太担心浏览器因为这种原因而造成的假死。 如果能为页面为单位,记录每个页面所需要的模块(代码),当下一次程序被运行时能提前将这些代码通过非阻塞的方式预加载到缓存(内存)中, 之后的代码加载都在内存中进行,虽然还是同步方式,但没有网络IO的操作,问题是不是解决了? jsvm 2.06 之后的 smartloader 模块(扩展)就是基于这个原理工作的。 开发人员依然只需考虑设计层面的问题,而不必去关心过多加载次数造成的麻烦。 诚如你所言,异步存在不少问题。所以我在与jindw讨论中也探讨过这个问题,我觉得同步本身是ok的,问题只在于浏览器阻塞。(由于我的项目的目标是所有平台,所以更把这个问题放在较低的优先级。)<script>标签其实也是同步的(不考虑defer的情况下),但是不阻塞。 但是除了你说的方法(缓存或预先编译),异步加载仍旧是可行的。jspkg这个项目是我看到的一个非常棒的js package管理实现,它可能是首先采用依赖计算后异步加载方式的。理论上说,只要预先知道所有依赖关系,就可以用异步方式加载。 |
|
返回顶楼 | |
发表时间:2007-11-08
wch3116 写道 Sean220 写道 如果大家在以下几个方面能达成共识就可以做到: (1)同步和异步import的函数定义 (2)js文件的目录组织和import包名的关系(比如目录层次即为包名,js文件名即为class名) (3)依赖关系的处理(包文件、头文件、配置文件、直接在原js里写) 没错!如今js framework很多,还有很多人都在设计自己的framework,如果这些framework之间能达成协议,采用相同规范的接口,那么基于这个规范,一样可以实现跨framework/container的js开发。 这个确实很难一致,因为在模型上可能有本质不同。例如我的PIES将区分package(namespace)和unit(module),这个模型和JS2.0(ECMAScript 4)是一致的。但是许多其它framework并不做区分(jsi就不做区分,或者说不推荐做区分),或区分是在别的意义上的(例如dojo),并不一致。 这个模型当然也会影响到包文件、头文件、配置文件的设计。 我个人的想法是这样的: import是不需要分同步和异步的。因为与jindw不同,我认为没有必要特别区分运行时依赖。(具体原因在过去一个讨论帖中写过了。)所以写在js中的$import语句,一定是同步加载所依赖的项目。(只有运行时依赖才可以采用异步加载。)而通过包文件、头文件之类外部定义依赖关系的,则理论上肯定可以异步加载,当然也可以用同步加载。由于不对运行时依赖做特别处理,所以无论异步还是同步,语义上并没有差异,只是实现不同而已。 |
|
返回顶楼 | |
发表时间:2007-11-08
wch3116 写道 我个人推荐尽量减少编译时的依赖,尽量使用运行时的动态依赖。 jsvm 在处理 $import 造成的回环,面临两个选择。 发现回环,抛出异常中断加载,或者忽略继续运行(因为头部的$import进来的模块不一定是编译时就需要的,也许是运行时才用到的) 最后jsvm暂时选择了前者。这个矛盾现在依然存在,是一个如何取舍问题,没有绝对的对与错。 如果要求运行时依赖,对于新写的库,可以做这种要求,对于老的代码库或者第三方库,很难去要求这一点。 实际上问题在于,按照我的理解,namespace管理和按需加载(load on demand),在本质上是两个需求。因此如果名称空间和加载单元是解耦的,则$import应该可以允许回环,只要它没有造成加载单元的回环。 而对于运行时依赖的问题,我认为这种需求比我们想象的要少。因为单向依赖的时候,区分运行时或者装载时依赖并没有什么特别不同。(前者装载顺序可以打乱,但是这有什么特别好处吗?)而双向依赖的时候,双向装载时依赖总是不能允许的(逻辑错误),而剩下的,无论是双向运行时依赖,还是双向运行时-装载时依赖,其实都要求两个装载单元需要总是被同时载入,(区别是,前者可以乱序,后者必须按一个顺序。)结果其实说明它们本应在一个装载单元中。也许有更复杂的例子里,有证据表明这种区分的价值,但是我没有看到过,而且我认为很可能不存在这样的例子,因为过于复杂的例子往往揭示了其装载单元切分上的不合逻辑。 注意,这里排除了那种真正的“插件机制”,也就是java中的Class.forName这样的。因为它不是一种代码组织方式,不是一种描述代码逻辑依赖关系的语法部件。这种需求应该被单独满足,而不应与import混淆起来,尽管其实现方式可能大同小异。举例来说,javeeye的页面要根据情况选择不同的编辑器,那就不应该在一个页面上写: $import(com.javaeye.webeditor.WYSIWYG); $import(com.javaeye.webeditor.BBQ); ... function pageLoaded() { if (...) { myEditor = new WYSIWYG(); } else { myEditor = new BBQ(); } } 也不能这样写: function pageLoaded() { if (...) { Editor = $import(com.javaeye.webeditor.WYSIWYG); } else { Editor = $import(com.javaeye.webeditor.BBQ); } myEditor = new Editor(); } 因为import应该是静态的(至少如果我们希望模仿java/c#/js 2.0等语言的语义的话)。 而应该这样写: function pageLoaded() { if (...) { editorPlugin = loadPlugin("WebEditor.WYSIWYG"); } else { editorPlugin = loadPlugin("WebEditor.BBQ"); } editorPlugin.init(); } 也就是采用独立于import的一种机制。当然plugin自己内部仍然可以用import进行代码组织。 |
|
返回顶楼 | |
发表时间:2007-11-08
zhourenjian 写道 无论预加载还是异步按需加载,如果控制粒度都是一个个问题。粒度的控制不应该是开发阶段作考虑,应该是在部署阶段进行配置的(当然ClassLoader需要提供控制粒度的配置),有专业人士进行分包打包并作优化。 你混淆了两个需求,import是用于代码逻辑组织的,与部署无关。部署时候的问题是若干个加载单元是否可以被打包成更大的部署文件被一次性下载到客户端。这两个问题当然总是有联系的,那就是部署单元通常不可能小于加载单元,而加载单元又不可能小于逻辑单元(或者说没有必要小于逻辑单元)。 zhourenjian 写道 异步按需加载并没有想象中的那么复杂,如果你已经完成一个ClassLoader,那么给ClassLoader一个改造,提供加载完毕的callback,那么可以用类似如下代码完成异步调用: ClassLoader.loadClass ("org.blahblah.HelloWorld", function () { org.blahblah.HelloWorld.sayHi(); )); 或者 ClassLoader.loadClass (["org.blahblah.HelloWorld","org.blahblah.utils.StringUtils"], function () { var s = org.blahblah.HelloWorld.sayHi(); var k = org.blahblah.utils.StringUtils (s); )); 在Java2Script的支持下,可以使用Java的匿名类作为相关callback类,还可以跨closure传递相关的变量对象,这样的话,对开发者不会有太大的异步恐惧的。 BTW:个人认为,需要专门的服务器端*.jsp或者servlet协助ClassLoader完成相关功能,是一个非常糟糕的设计。 我认为老万说的很好 wch3116 写道 “异步” 不仅打乱了开发人员的一般习惯思维方式,同时也改变了程序顺序加载执行最朴素的规律。 对于模块之间的静态依赖,或者程序对模块的静态需求,采用预加载的方式,然后通过事件侦听或者回调程序入口来继续加载后的的程序执行尚且可以。 但这些依赖或者需求是程序运行时动态决定的,而采用异步的方式肯定造成程序分支增多,增加了系统的复杂性。 我帮他说得更透一点,问题不在于说到异步调用的转换有多难,而在于对形式的要求,会干扰程序员。这也是为什么jindw以“无侵入性”作为其框架的根本出发点的原因。我再补充一点,改为异步后,我们的代码就变得依赖于框架,这对测试来说是很讨厌的一件事情。 Java2Script是一个不同的模式,作为代码生成来说,上述问题不是很重要,但是对于本身的js框架来说,就不能接受了。 关于你说的服务器端的问题。假如逻辑单元甚至加载单元要与服务器相关,是很糟糕的。但是部署单元与服务器相关是ok的。部署策略可以由服务器计算出来乃至动态适应的。 |
|
返回顶楼 | |