随着浏览器功能愈来愈完善,前端已经不只仅是切图作网站,前端在某些方面已经媲美桌面应用。愈来愈庞大的前端项目,愈来愈复杂的代码,前端开发者们对于模块化的需求空前强烈。后来node出现了,跟随node出现的还有commonjs,这是一种js模块化解决方案,像Node.js主要用于服务器的编程,加载的模块文件通常都已经存在本地硬盘,因此加载起来比较快,不用考虑异步加载的方式,CommonJS 加载模块是同步的,因此只有加载完成才能执行后面的操做。可是浏览器环境不一样于Node,浏览器中获取一个资源必需要发送http请求,从服务器端获取,采用同步模式必然会阻塞浏览器进程出现假死现象。在这方面dojo曾经作了伟大尝试,早期dojo即是采用xhr+eval的方式,结果可想而知,阻塞现象是必然的。后来出现无阻塞加载脚本方式在开发中普遍应用,在此基础结合commonjs规范,前端模块化迎来了两种方案:AMD、CMD.javascript
借用三藏法师一句话:人是人他妈生的,妖是妖他妈生的。此话虽不雅,但用这里却颇为贴切。AMD 是 RequireJS 在推广过程当中对模块定义的规范化产出,CMD是SeaJS 在推广过程当中被普遍认知。RequireJs出自dojo加载器的做者James Burke,SeaJs出自国内前端大师玉伯。两者的区别,玉伯在12年如是说:html
RequireJS 和 SeaJS 都是很不错的模块加载器,二者区别以下: 1. 二者定位有差别。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。SeaJS 则专一于 Web 浏览器端,同时经过 Node 扩展的方式能够很方便跑在 Node 服务器端 2. 二者遵循的标准有差别。RequireJS 遵循的是 AMD(异步模块定义)规范,SeaJS 遵循的是 CMD (通用模块定义)规范。规范的不一样,致使了二者 API 的不一样。SeaJS 更简洁优雅,更贴近 CommonJS Modules/1.1 和 Node Modules 规范。 3. 二者社区理念有差别。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。SeaJS 不强推,而采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。 4. 二者代码质量有差别。RequireJS 是没有明显的 bug,SeaJS 是明显没有 bug。 5. 二者对调试等的支持有差别。SeaJS 经过插件,能够实现 Fiddler 中自动映射的功能,还能够实现自动 combo 等功能,很是方便便捷。RequireJS 无这方面的支持。 6. 二者的插件机制有差别。RequireJS 采起的是在源码中预留接口的形式,源码中留有为插件而写的代码。SeaJS 采起的插件机制则与 Node 的方式一致:开放自身,让插件开发者可直接访问或修改,从而很是灵活,能够实现各类类型的插件。.
关于两者的区别,前人之述备矣:前端
让咱们再聊聊浏览器资源加载优化node
而在本文,咱们仅从代码爱好者的角度来一窥两者API、模块管理、加载、执行的异同。浏览器
对比AMD与CMD规范,两者最大的区别在于依赖模块的执行时期,CMD规范中明确要求延迟执行(Execution must be lazy.)。这一点从两者在模块的定义方法define的函数签名上能够看出:服务器
define(id?, dependencies?, factory);
define(["./a", "./b"], function(a, b) { //BEGIN a.doSomething(); b.doSomething(); });
CMD中模块以下定义:
define(function(require, exports, module) { // The module code goes here });
define(function(require, exports, module) { //BEGIN require("./a").doSomething(); require("./b").doSomething(); });
须要提一下的是两者对待依赖模块的加载是一致的,在factory执行时,依赖模块都已被加载。从代码上来看,AMD中在BEGIN处a、b的factory都是执行过的;而CMD中虽然a、b模块在BEGIN已被加载,但还没有执行,须要调用require执行依赖模块。这就是CMD中着重强调的延迟执行。若是这个例子不明显的话,咱们来看一下条件依赖:
AMD:
define(["./a", "./b"], function(a, b) { //BEGIN if (true) { a.doSomething(); } else { b.doSomething(); } //END });
CMD:
define(function(require) { // BEGIN if(some_condition) { require('./a').doSomething(); } else { require('./b').soSomething(); } // END });
条件依赖意思是咱们根据条件使用依赖项,在AMD中BEGIN位置处a、b模块都须要被执行一次。CMD中BEGIN处a、b都没有被执行,在END处,a、b只有一个被实际执行过。
那么问题来了,javascript做为脚本语言,代码确定是顺序执行的,做为AMD与CMD的实现者,requireJs与seaJs是如何知道须要加载的全部文件呢?又是如何作到异步加载?对于seajs,factory中代码确定是顺序执行的,可是这必须致使require时的阻塞加载,而她又是如何保证异步加载的?
每个卓越的思想都有一份朴实的代码实现。因此不管AMD与CMD都要面临如下几个问题:
scriptElement= document.createElement('script'); scriptElement.src = moduleUrl; scriptElement.async = true; scriptElement.onload = function(){.........}; document.head.appendChild(scriptElement);
五、模块加载完毕后,获取依赖项(amd、cmd区别),改变模块status,由statuschange后,检测全部模块的依赖项。
因为requirejs与seajs遵循规范不一样,requirejs在define函数中能够很容易得到当前模块依赖项。而seajs中不须要依赖声明,因此必须作一些特殊处理才可否得到依赖项。方法将factory做toString处理,而后用正则匹配出其中的依赖项,好比出现require(./a),则检测到须要依赖a模块。
同时知足非阻塞和顺序执行就须要须要对代码进行一些预处理,这是因为CMD规范和浏览器环境特色所决定的。
六、若是模块的依赖项彻底加载完毕(amd中须要执行完毕,cmd中只须要文件加载完毕,注意这时候的factory还没有执行,当使用require请求该模块时,factory才会执行,因此在性能上seajs逊于requirejs),执行主模块的factory函数;不然进入步骤3.
最后,不管requireJs仍是seaJs都已被普遍应用于web开发中,实际选取时应根据如下几方面综合平衡选取: