(本文译自 What Is AMD, CommonJS, and UMD?)
多年来,可供选择的JavaScript组件的生态系统不断地稳步增加。有很多的选择固然是很好的一件事,但是各个组件混合搭配使用的时候会带来不少的问题,开发者不会花很多时间就会发现所有组件使用起来总有这样那样的问题。
为了解决这些问题,互为竞争对手的模块规范 AMD 和 CommonJS 出现了,它们可以让开发者在约定的沙箱以模块化的方式编写自己的代码,以免“污染生态系统”。
异步模块定义(英文简称AMD)已经引领了前端潮流,RequireJS已经是最流行的实现方式。
下面的例子是 foo
模块简单地依赖 jquery
。
// filename: foo.js
define(['jquery'], function ($) {
// methods
function myFunc(){};
// exposed public methods
return myFunc;
});
下面的更复杂一点的例子就是多个依赖和多个暴露方法的用法。
// filename: foo.js
define(['jquery', 'underscore'], function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
});
定义的第一部分是依赖的数组,而第二部分基本上是仅在第一部分声明好才能执行的回调函数。(像 RequireJS 这种脚本加载器才会关心这部分,包括找出依赖文件的位置)
注意:定义中的依赖顺序很重要!(比如 jQuery
---> $
, underscore
---> _
)
还要注意的是,我们可以映射依赖到我们想要的变量上。如果我们将上面代码中的 $
改为 $$
,那我们下面代码的函数块中引用到 jQuery
时都得用 $$
代替 $
。
最重要的一点是:你绝对不能在上述代码外的函数中引用变量 $
和 _
,因为它对于外面来说就是一个不透明的沙箱。这就是那些规范想要达到的目标!
如果你用过 Node.js
写过代码,那你会对 CommonJS
感到熟悉(因为就是只有一些轻微的变动)。它已经变成使用 Browserify
开发的前端开发者中的一种趋势。
用跟上面一样的格式,下面就是采用 CommonJS
规范的 foo
模块写法。
// filename: foo.js
// dependencies
var $ = require('jquery');
// methods
function myFunc(){};
// exposed public method (single)
module.exports = myFunc;
下面是应用了多依赖和多个暴露方法的复杂例子:
// filename: foo.js
var $ = require('jquery');
var _ = require('underscore');
// methods
function a(){}; // private because it's omitted from module.exports (see below)
function b(){}; // public because it's defined in module.exports
function c(){}; // public because it's defined in module.exports
// exposed public methods
module.exports = {
b: b,
c: c
};
虽然 CommonJS
和 AMD
的风格同样大受欢迎,但是看起来似乎它们并没有达成共识。这样的局面也导致了一种能同时支持两种风格的需要出现,这带给了我们通用模块定义。
下面这种模式诚然丑陋,但是能使 AMD
和 CommonJS
和谐相处,还支持老式的 global
变量定义。
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
// methods
function myFunc(){};
// exposed public method
return myFunc;
}));
保持同样的模式实现更复杂的例子:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery', 'underscore'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'), require('underscore'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery, root._);
}
}(this, function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
}));