关于综述部分可见:第五届 d2 相关介绍 ,需要知道的是模块加载已经有语言级的提案 ,正是由于它是语言级,估计很长时间内都不会实现。而由民间形成的事实规范 commonJS 则是非常可行,恰当的比喻是 ecma4 语言级的类继承机制与目前 javascript 模拟的类继承。
先行者:
最先引入模块管理加载的据说是 dojo ,不过一直没有用过,采用的 xhr 下载脚本文件 eval,这种方案由于跨域情景导致了不少问题。
yui2 从 2 后期引入了 loader ,到 yui3 已经形成了比较成熟的加载方案 ,利用动态添加脚本标签来实现下载解析,通过串行下载来避免复杂的依赖管理(目前来看也是缺点),另一个问题就是组件集中配置过于冗余 。
领先者:
目前最流行的应该是 LABjs (oldj's analysis ),但本质上讲它只能算作脚本文件加载工具,并不涉及模块管理方面,对于大型应用似乎力所不及。
最“规范”的要算 requireJS ,自己的实现部分提案 成了commonJS浏览器端的异步模块加载规范。其中的一个亮点就是通过参考其他语言机制关于约定模块与文件路径的对照关系 来解决中央配置冗余。
代码也确实比较复杂,充分利用了浏览器并行下载文件 ( Roundup on Parallel Connections )的特性,当一个文件代表一个模块时,采用动态加载,可以省略模块名:
define(function(){ return { modulePart:"part" }; });
如果想提高性能,部分模块进行静态载入时,提供了编译脚本 ,在 rhino 解释引擎下调用 cc 的解析 java 程序 ,parse 出 AST 后将文件名加入到define 参数中 :
define(“moduleName”,function(){ return { modulePart:"part" }; });
这也是令我十分激动的地方,用 js 写个语法解析器 确实不是那么简单,那么对于这种后端处理调用现有 java 的成熟程序,既充分利用了 js 的灵活性,也吸取了静态语言的严谨性。
方案:
js 动态加载的问题是:下载完毕就会立即进行脚本的解析执行,从而如果要利用并行的同时又要处理好依赖关系则只能通过 function wrapper (来源于 module pattern )来解决,模块的代码通过一个匿名函数包装,从而实现下载和模块的执行分离。
依赖解决:
require(["module1","module2"],callback);
基本思想即是:在下载一个模块完毕后,并行下载其依赖的模块文件,而如果一个模块没有依赖项则立即初始化自己,并通知其他依赖自己的模块。其中下载依赖,依赖下载完毕后再下载依赖的依赖(直至没有依赖)是一个递归的过程,而关键在于如何一层层从底层开始通知顶层模块依赖的加载结束。
实现方式分为两类:
1.内部函数
将依赖项通知函数 check 定义在模块加载函数中,通过闭包将依赖项和其通知的模块绑定在一起。
function attach(mod,callback){ var requires=mod.requires; for each( r : requires){ attach(r,check) } //作用域链保存了过多内容 function check(){ attach mod.requires if added if( mo.requires all is loaded) { init mod callback } } scriptLoad(mod.path,check); }
优点是:程序简单清晰,缺点则是内部函数定义导致的过多内容被存放在函数作用域中,内存问题。
2.requireJS 采取的等待队列
将通过递归进行的逐层向上回调通过程序显式的等待回调集合来模拟(提高了复杂度),优点则是比定义内部函数闭包更节省内存:
var queuedcallbacks; attach(mod) { scriptload(r.path,load); } main(mod,deps){ //保存内容较少 function callback(){ mod.dep++; if(mod.dep==deps.length) execmod(mod) } foreach(d:deps){ queuedcallbacks[d].push(callback); } if(mod.dep==deps.length) execmod(mod) } execmod(mod){ init mod //通知等待该模块的模块 foreach( func in queuedcallbacks[mod]) func(); } load(){ mode=get mod from script node deps=get mod's dependence main(mod,deps); foreach( d: deps){ attach(d) } }
API 设计:
refer :
Loading Scripts Without Blocking
Jquery 也要支持 commonjs AMD :
Standards and proposals for JavaScript Modules and jQuery
新的 proposal ?