快速理解 js 模块规范

随着前端的日益壮大,前端模块化的编程成为了一个非常重要的东西,这可以更好的帮我们构造工程化的前端项目。

说道前端模块规范,我们耳熟能详的就这三种 CommonJS, AMD, CMD

CommonJS

首先说一下CommonJS, node.js 的模块系统就是参照 CommonJS 规范实现的。在CommonJS 中有一个全局性的方法require(path),我们通过方法加载需要的模块。

具体的用法如下:

声明模块:

1
2
3
4
5
6

// test.js

exports.test = function(){
console.log('test')
}

引用模块:

1
2

var test = require('test');

CommonJS 规范中,require() 用来引入外部模块,exports ·对象用于导出当前模块的方法或变量,作为唯一的到出口,module 对象就代表模块本身。

CommonJS 主要用在了服务端,因为服务端加载,读取模块都是在本地加载,如果浏览器中使用,比方说上面的代码,网盘,我们在加载 test 模块的时候必须等待他加载完才能继续执行后面的代码,这在服务端没什么问题,但在浏览器中,我们需要把模块下载下来,才能去执行后面的代码,在等待的过程中,浏览器处于“假死”的状态。

为了能够异步的加载模块,于是 AMD 规范就诞生了。

AMD

最常用的RequireJS就是实现了AMD规范。

require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。

如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。

1
2
3
4
5
define(['dep1','dep2'],function(
dep1,dep2
){

})

加载模块,与 CommonJS 不同的是需要在require中填入回调函数,第一个参数指明需要加载的模块

1
2
3
4
5
 require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){

    // some code here

  });

CMD

玉伯写的 seajs 就是遵循CMD规范写的

1
2
3
define(function(require,exports,module){

})

他与 AMD 存在着一定的区别。

比方说我们需要在运行代码的时候先后加载两个模块:

AMD

1
2
3
4
5
6
7
8

require(['block1','block2'],function(block1,block2){
block1.start();
block1.end();

block2.start();
block2.end();
})

AMD 虽然实现了异步加载,但是在开始的时候就把所有依赖写出来,可能不太符合我们平时书写的一个逻辑顺序,是否能够像 commonJS那样用的时候require,而且还是支持异步加载后再执行呢。

CMD 就是支持的

CMD

1
2
3
4
5
6
7
8
9
10
11
12

define(function(require, exports, module){
var block1 = require('block1');

block1.start();
block1.end();

var block2 = require('block2');
block2.start();
block2.end();

})

CMD 只有当代码执行到 require的时候才会去异步的加载模块,不会像AMD一开始就把需要的模块都加载好才开始执行。

AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。

总结

个人认为,在实际的开发中,大多还是用 AMD 规范的,使用 AMD 规范你能够更清楚的看到 模块之间的依赖关系,能够更符合程序员的思维方式。博主暂时也还没有用过 CMD 规范。

ES6 中的 import webpack 也是将其转化为 AMD 规范的。