js 代码 2009 年 HTML5 兴起后,前端代码的行数已经呈现井喷式发展,随着代码量的增加,模块的缺失的缺点日益凸显,Javascript 社区做了很多探索。如今 JavaScript 模块化编程的概念已经普及开来,一提起模块化,大家想到的可能是 AMD,CMD,requirejs 或 seajs。其实还有很多其他的概念。本文将会陈述下 JavaScript 模块的前世今生。

模块

模块,又称构件,是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)

一个独立的模块需要能够独立完成一个功能,可以引用依赖和被依赖。例如 C 语言中的库和头文件,Java 中的包,等等。

原始写法

一个函数实际上就是一个模块

1
2
3
4
// 最简单的函数,可以称作一个模块 
function add(x, y) {
return x + y;
}

一个更合理的模拟模块方式

1
2
3
4
(function (mod, $, _) {
mod.add = ***;
mod.sub = ***;
}((window.mod = window.mod || {}), jQuery, Underscore));

这依旧不完美,因为这个模块必须要先依赖 jQuery 库。理想的情况是,模块的依赖顺序是随意的,我们可以随机顺序指定依赖而不用担心定义先后的问题。

CMD (Common Module Definition)

说道 CMD 就不能不提 commonjs,提到 commonjs 就不能不提 node

CMD 规范参照 commonjs 中的方式,定义模块的方式如下:

1
2
3
define(function(require, exports, module) {
// The module code goes here
});

一个文件就是一个模块,文件名就是模块的名字,使用模块的方法也和 commonjs 中一致,只需 require 就好了,模块名字可省略后缀。

1
2
// 使用 event.js 模块 
var ec = require('event');

CMD 的典型实现就是 seajs,应用的很广泛。

AMD (Asynchronous Module Definition)

AMD) 是异步模块定义,特别适合在浏览器端使用,其规范和 CMD 是很像的,AMD 规范中定义模块的方式如下:

1
define(id?, dependencies?, factory);

同 CMD 一样,一个文件即一个模块,模块的使用方法如下:

1
2
3
define(["beta"], function (beta) {
bata.***// 调用模块
});

AMD 主张依赖注入,这点和 CMD 不同(以来查找)。

AMD 也支持已 CMD 的方式来使用依赖。

AMD 的典型实现有 requireJSmodJSlodJS

ES6

ES6 带来了语言层面的模块化支持,规范方面见 这里,文档方面见 这里。=

UMD

UMD 的全称是 Universal Module Definition。和它名字的意思一样,这种规范基本上可以在任何一个模块环境中工作。

一段典型的 UMD 代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(function (root, factory) {
var Data = factory(root);
if ( typeof define === 'function' && define.amd) {
// AMD
define('data', function() {
return Data;
});
} else if ( typeof exports === 'object') {
// Node.js
module.exports = Data;
} else {
// Browser globals
var _Data = root.Data;

Data.noConflict = function () {
if (root.Data === Data) {
root.Data = _Data;
}

return Data;
};
root.Data = Data;
}
}(this, function (root) {
var Data = ...
// 自己的代码
return Data;
}));

这是出自 data.js 中的一部分代码,其原理就是做个判断,不同的环境进行不同的处理。

总结

模块化的探索,使前端工程化成为了可能,可以说没有模块,工程化更无从弹起,