目的是在项目框架中安装外部组件时,组件的规范能够统一。
由于早期开发未整体文档,细节已记不太清,先把代码地址贴上来:https://github.com/tianmiyingzi/feather2-command-install
需要了解的知识点:AMD规范,CMD规范,Comonjs规范,UMD规范等。
以下是调研知识点笔记:
Javascript模块化编程
问:为什么要模块化?
答:提高代码重复利用率。
问:怎么样模块化?
答:模块化的经历过程:函数简单的放在一起→对象写法→立即执行函数写法→放大模式→宽放大模式(Loose augmentation)→输入全局变量
1."污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系
2. 暴露所有模块成员,内部状态可以被外部改写
3. "宽放大模式"就是"立即执行函数"的参数可以是空对象
关键字:
"立即执行函数"(Immediately-InvokedFunction Expression,IIFE
CommonJS规范
CommonJS主要是服务器端模块的规范,Node.js采用了这个规范。
每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性。
概念(起源):
官方JavaScript标准定义的API是为了构建基于浏览器的应用程序。然而,并没有定于一个用于更广泛的应用程序的标准库。
CommonJS API定义很多普通应用程序(主要指非浏览器的应用)使用的API,从而填补了这个空白。它的终极目标是提供一个类似Python,Ruby和Java标准库。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用可以运行在不同的JavaScript解释器和不同的主机环境中。
在兼容CommonJS的系统中,你可以实用JavaScript程序开发:
服务器端JavaScript应用程序
命令行工具
图形界面应用程序
混合应用程序(如,Titanium或Adobe AIR)
内容:
1.在一个模块中,存在一个自由的变量”require”,它是一个函数。
这个”require”函数接收一个模块标识符。
“require”返回外部模块所输出的API。
如果出现依赖闭环(dependency cycle),那么外部模块在被它的传递依赖(transitive dependencies)所require的时候可能并没有执行完成;在这种情况下,”require”返回的对象必须至少包含此外部模块在调用require函数(会进入当前模块执行环境)之前就已经准备完毕的输出。(译者:如果难理解,看下面的例子。)
如果请求的模块不能返回,那么”require”必须抛出一个错误。
2. 在一个模块中,会存在一个名为”exports”的自由变量,它是一个对象,模块可以在执行的时候把自身的API加入到其中。
3. 模块必须使用”exports”对象来做为输出的唯一表示。
AMD规范
框架是RequireJS
// filename: foo.js
define(['jquery', 'underscore'], function($, _) {
// methods
function a(){}; // private because it's not returned (seebelow)
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
});
define 是AMD规范用来声明模块的接口,示例中的第一个参数是一个数组,表示当前模块的依赖。第二个参数是一个回调函数,表示此模块的执行体。只有当依赖数组中的所有依赖模块都是可用的时,AMD模块加载器(比如RequireJS)才会去执行回调函数并返回此模块的暴露接口。
注意,回调函数中参数的顺序与依赖数组中的依赖顺序一致。(即: jquery -> $, underscore -> _ )
当然,在这里我可以将回调函数的参数名称改成任何我们想用的可用变量名,这并不会对模块的声明造成任何影响。
除此之外,你不能在模块声明的外部使用 $ 或者 _ ,因为他们只在模块的回调函数体中才有定义。
CMD规范
CMD是国内玉伯大神在开发SeaJS的时候提出来的,属于CommonJS的一种规范
UMD规范
umd是AMD和CommonJS的糅合
AMD 浏览器第一的原则发展 异步加载模块。
CommonJS 模块以服务器第一原则发展,选择同步加载,它的模块无需包装(unwrapped modules)。
这迫使人们又想出另一个更通用的模式UMD (Universal Module Definition)。希望解决跨平台的解决方案。
UMD先判断是否支持Node.js的模块(exports)是否存在,存在则使用Node.js模块模式。
在判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。
(function (window, factory) {
if (typeof exports === 'object') {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define(factory);
} else {
window.eventUtil = factory();
}
})(this, function () {
//module ...
});
CommonJS与AMD比较
CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
AMD规范则是非同步加载模块,允许指定回调函数。
由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范
AMD与CMD比较
1、二者都是异步模块定义(AsynchronuousModule Definition)的一个实现;
2、AMD和CMD都是CommonJS的一种规范的实现定义,RequireJS和SeaJS是对应的实践;
3、AMD和CMD的区别:
1.CMD推崇依赖就近,AMD推崇依赖前置:CMD相当于按需加载,定义一个模块的时候不需要立即制定依赖模块,在需要的时候require就可以了,比较方便;而AMD则相反,定义模块的时候需要制定依赖模块,并以形参的方式引入factory中。
2.对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)。
3. AMD的api默认是一个当多个用,CMD严格的区分推崇职责单一。例如:AMD里require分全局的和局部的。CMD里面没有全局的require,提供 seajs.use()来实现模块系统的加载启动。CMD里每个API都简单纯粹。
区别看下边例子:
//AMD方式定义模块
define(['dep1','dep2'],function(dep1,dep2){
//内部只能使用制定的模块
return function(){};
});
//CMD
define(function(require,exports,module){
//此处如果需要某XX模块,可以引入
var xx=require('XX');
});
4、JavaScript语言是弱结构性的,通过CommonJS定义一些规范,CMD和AMD得定义模块的方式对代码进行管理,使得更易维护;此外,NodeJS的诞生,对模块的规范定义,和包(npm)的概念的引入,让JS管理不再困难!
CommonJS依赖闭环解释
因为node.js完全实现了CommonJS Modules/1.0规范,那么我们用其来解释CommonJS Modules/1.0中的依赖闭环问题。看如下代码:
a.js
console.log('a starting');
exports.done = false;
var b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
b.js
console.log('bstarting');
exports.done= false;
var a =require('./a.js');
console.log('inb, a.done = %j', a.done);
exports.done= true;
console.log('bdone');
main.js
console.log('main starting');
var a = require('./a.js');
var b = require('./b.js');
console.log('in main, a.done=%j,b.done=%j', a.done, b.done);
当main.js加载a.js的时候,a.js加载b.js,同时,b.js想要加载a.js,这时候就产生了依赖闭环的问题,为了避免无限循环,需要打破这个闭环。根据CommonJS Modules/1.0规范中的说明「在这种情况下,”require”返回的对象必须至少包含此外部模块在调用require函数(会进入当前模块执行环境)之前就已经准备完毕的输出。」,有些绕,让我们从依赖闭环产生的地方跟踪,b.js需要require a.js,这里b.js做为当前模块,a.js相对于b.js来说是外部模块,那么a.js的输出应该是在其require b.js之前(即「进入当前模块执行环境」)就应该返回,执行过程如下:
a.js
console.log('a starting'); exports.done = false; // 只执行到这里,然后exports返回给调用模块(b.js),以下被丢弃 var b = require('./b.js'); console.log('in a, b.done = %j', b.done); exports.done = true; console.log('a done'); |
|
然后b.js继续执行完成。以下是执行结果:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true
注意,虽然main.js同时require了a.js和b.js,但是根据node.js的模块缓存策略,模块只执行一次。
以下是调研工具笔记:
1.amd-to-common
Github地址:https://github.com/Willyham/amd-to-common
很单一
不管位置,只寻找第一个defined定义,并分析转化。第一个defined前后的内容会自动过滤。
2. recast-to-cjs
http://skookum.com/blog/converting-a-project-from-amd-to-cjs-with-recast
Github地址:https://github.com/Skookum/recast-to-cjs
涵盖的形式较多。
没有运行起来
这两个主要是看思路的,思路和都和上面差不多
3. 基于browserify的插件unamd
https://www.npmjs.com/package/unamd
4. 基于browserify的插件browserify-ftw
https://github.com/thlorenz/browserify-ftw/blob/master/examples/RyanFitzer-RequireJs-Jquery.md#initializing-the-npm-package-and-installing-dependencies
这个是转成UMD或者混合形式
5. uRequire
https://github.com/anodynos/uRequire
1.amd-to-common
Github地址:https://github.com/Willyham/amd-to-common
很单一
不管位置,只寻找第一个defined定义,并分析转化。第一个defined前后的内容会自动过滤。
2. recast-to-cjs
http://skookum.com/blog/converting-a-project-from-amd-to-cjs-with-recast
Github地址:https://github.com/Skookum/recast-to-cjs
涵盖的形式较多。
没有运行起来
这两个主要是看思路的,思路和都和上面差不多
3. 基于browserify的插件unamd
https://www.npmjs.com/package/unamd
4. 基于browserify的插件browserify-ftw
https://github.com/thlorenz/browserify-ftw/blob/master/examples/RyanFitzer-RequireJs-Jquery.md#initializing-the-npm-package-and-installing-dependencies
这个是转成UMD或者混合形式
5. uRequire
https://github.com/anodynos/uRequire
工具调研
1.amd-to-common
Github地址:https://github.com/Willyham/amd-to-common
很单一,只能完成单一形式的转化。
不管位置,只寻找第一个defined定义,并分析转化。第一个defined前后的内容会自动过滤。
2. recast-to-cjs
http://skookum.com/blog/converting-a-project-from-amd-to-cjs-with-recast
Github地址:https://github.com/Skookum/recast-to-cjs
涵盖的形式较多。
没有运行起来
3. 基于browserify的插件unamd
https://www.npmjs.com/package/unamd
4. 基于browserify的插件browserify-ftw
5. uRequire
https://github.com/anodynos/uRequire
a tool for javascript module formatconversion - AMD/Requirejs --> commonjs/nodejs and back (plus more).
// with a list of dependencies
define(['react'], function(React) {
// optional return value which is the module itself;
return React.createClass({});
});
// with a variable listing dependencies.This is not recommended.
var DEPENDENCIES = ['react'];
define(DEPENDENCIES, function(React) {
return React.createClass({});
});
// with no dependencies
require(function() {
return {};
});