浅谈 JavaScript 模块定义规范

JS 模块定义常见的有三种方式,即 AMD, CMD 和 CommonJS。其实还有一个 UMD,他是 CommonJS 和 AMD 揉和在一起而已。不过这些都 out 了,拥抱 ES6 吧。话虽这么说,你让那些不用 ES6 不用 babel 的怎么活,所以还是要了解下滴。

CommonJS

CommonJS 是服务端即 Node.js 采用的模块化方案,我们应该都很熟悉了。例如:

const fs = require('fs');
fs.readFileSync();

这个过程是同步的,只有成功加载 fs 后才能执行后面的步骤。但在服务器文件都在本地,所以这个问题不大。但这个在浏览器就不合适了,如果文件加载耗时很长,将导致一直等待。

AMD

AMD 全称 Asynchronous Module Definition,意思就是异步模块定义。
用法如下:

require(['math'], function(math) {
    math.add(1, 2);
});

math 模块的加载和 math.add() 方法的执行不是同步的,这样浏览器就不会假死。
RequireJs 和 CurlJs 实现了 AMD 规范,将他们嵌入网页,就可以在浏览器端进行模块化编程了。
关于 AMD 的详细模块定义可以参考wiki。这里给出 Underscore 的 AMD 定义方法:

if (typeof define == 'function' && define.amd) {
    define('underscore', [], function() {
        return _;
    });
}

CMD

CMD 全称 Common Module Definition,意思就是通用模块定义。。
对于依赖的模块,AMD 是提前执行,而 CMD 是延迟执行。AMD 推崇依赖前置,而 CMD 则推崇依赖就近。例如:

define(function(require, exports, module) {
    const math = require('./math');
    math.add(1, 2);
});

CMD 的主要实现是 SeaJS。

UMD

UMD 全称 Universal Module Definition。
UMD 是 AMD 和 CommonJS 的揉和,他优先使用 CommonJS 的加载方式,其次才使用 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 ...
});

其实就是一个服务端和浏览端通用的模块解决方案。

ES6 Module

ES6 在语言规格的层面上实现了模块功能,并且实现非常简单,完全可以替代现有的模块加载方案,成为浏览器和服务端都通用的模块解决方案。

import {stat, exists, readFile} from 'fs';

这种做法将只在 fs 模块加载3个方法,其他方法不会进行加载。ES6 可以在编译时就完成模块加载,效率比 CommonJS 的加载方式高。
在浏览器中使用 ES6 模块的语法:

<script type="module" src="foo.js"></script>

目前 Node 默认模块格式是 CommonJS,ES6 的模块方案还不支持,但可以通过 babel 来使用。

现在推荐做法是广泛使用 ES6 甚至 ES7 来书写 JavaScript 以提高开发效率,再使用 babel 转码就好了。所以前后端我们也都可以使用 ES6 的 Module 来进行。其他的模块加载方案应该会渐渐退出历史舞台。


ECMAScript6 入门 Module