导航
导航
文章目录
  1. CommonJS
  2. AMD
    1. define函数
    2. require函数
  3. CMD
    1. define函数
      1. require
      2. export
      3. module
  4. AMD与CMD的区别
  5. RequireJS与SeaJS的区别

前端模块化小结

随着我们的项目越来越复杂,参与项目的人员越来越多,将项目划分模块就变得十分重要,开发者只需要实现自己负责的功能,其他部分可以加载别人已经写好的模块。但是JavaScript不是一种模块化的编程语言,所以JavaScript开发人员做了很多努力,在现有的运行环境中实现了模块化的功能。

前端的模块化开发主要解决两个问题:命名冲突和文件依赖。在前端领域探究模块化开发的过程中,产生了不同的js模块规范,目前通用的JavaScript模块规范主要是:CommonJSAMD,先从CommonJS说起。

CommonJS

CommonJS是服务器端的模块的规范,NodeJS将其发扬光大,标志着JavaScript模块化编程正式登上舞台。

  1. 定义模块:CommonJS规范规定一个单独的文件就是一个模块,每一个模块都是一个单独的作用域,在该模块内部的变量无法被其它模块读取,除非定义为global对象的属性。
  2. 模块输出:想要输出模块中的变量使用module.exports对象,该对象是模块外部与内部通信的桥梁。
  3. 加载模块:使用require方法加载模块,该方法读取一个文件并执行,最后返回文件内部的module.exports对象。

下面举个例子:

1
2
3
4
5
6
7
8
9
//定义模块 module.js
var age = 23;
function printAge() {
console.log(age);
}
//模块输出
module.exports = {
printAge: printAge
}

1
2
3
//加载模块
var myModule = require('./module.js');
myModule.printAge();

加载模块时,一般可以省略js扩展名,可以使用相对路径也可以使用绝对路径。

然而CommonJS模块加载是同步的,这在服务器端很容易实现,但是在浏览器端实现起来会出现很多问题。

AMD

AMD(Asynchronous Module Definition)翻译成中文是异步模块定义,它是一个在浏览器端模块化开发的规范。由于不是JavaScript原生支持的,使用AMD规范进行页面开发需要用对应的库,RequireJS就是其中最出名的一个,实际上,AMD是RequireJS在推广过程中对模块定义的规范化的产出。

RequireJS主要解决了两个问题:

  1. 实现js文件的异步加载,避免网页失去响应。
  2. 管理模块之间的依赖性,便于代码的编写和维护。

define函数

RequireJS定义了一个define函数,它是一个全局变量,用来定义模块,描述如下:

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

  1. id:可选参数,定义中模块的名字,若没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字。
  2. dependencies:一个当前模块依赖的,已被模块定义的模块标识的数组字面量。该参数可选,若忽略该参数,它默认为[“require”, “exports”, “module”]。
  3. factory:模块初始化要执行的函数或对象。若为函数,它应该只被执行一次。若为对象,该对象应该为模块的输出值。

require函数

RequireJS还定义了一个require函数用来加载模块,描述如下:

1
require([dependencies], function(){});

require函数接受两个参数,第一个参数是一个数组,表示所依赖的模块。第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块要以参数形式传入该函数,在回调函数内部才可以使用这些模块。

require函数在加载依赖模块的时候是异步加载的,这样浏览器就不会失去响应,它指定的回调函数,也只有在前面的模块都加载成功后,才会运行,这样就解决了依赖性的问题。

CMD

CMD(Common Module Definition)翻译成中文就是通用模块定义。该规范明确了模块的基本书写格式和基本交互规则。该规范是在国内发展出来的。和AMD一样,使用CMD规范也需要相应的库函数,SeaJS就是其中一个。实际上,CMD是SeaJS在推广过程中对模块定义的规范化的产出。SeaJS要解决的问题和RequireJS一样,只不过在模块定义方式和模块加载的时机上有些区别。

define函数

CMD规范的define函数和AMD规范的描述相同,也是:

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

但是CMD推崇一个文件一个模块,所以经常用文件名作为模块id。同时,CMD推崇就近依赖,所以一般不在define函数的参数中写依赖,而是在factory函数中就近写。

factory函数有三个参数:

1
factory(require, exports, module)

require

require是一个方法,它接受模块标识作为唯一的参数,用来导入其他的模块,获取其它模块提供的接口。

export

exports是一个对象,用来向外部提供接口。

module

module是一个对象,存储了去模块相关联的一些属性和方法。

AMD与CMD的区别

首先我们知道,AMD是RequireJS 在推广过程中对模块定义的规范化产出。CMD是SeaJS在推广过程中对模块定义的规范化产出。他们的区别主要有以下四点。

1、对于依赖的模块,AMD是提前执行(注意是执行不是加载),CMD是延迟执行。RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不同)。AMD和CMD都是异步加载的,只是AMD在加载模块完成后就会执行该模块,所有模块都加载执行完后会进入require的回调函数,执行主逻辑,这样就会导致依赖模块的执行顺序和书写顺序不一定一致,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才会执行。而CMD在加载完某个依赖模块后并不执行,只是下载下来,在所有依赖模块加载完成后再进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的。

2、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块。CMD推崇就近依赖,只有在用到某个模块的时候再去require。对比如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// AMD
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething()
b.doSomething()
//...
})
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
var b = require('./b') // 依赖可以就近书写,需要用到的时候才require
b.doSomething()
// ...
})

3、AMD的API根据使用范围有区别,但使用同一个API接口。CMD的API严格区分,推崇职责单一。

4、AMD是异步并行加载,在AMD的规范下,同时异步加载是不会产生错误的。而CMD的机制不同,这种加载方式会产生错误,但是如果能规范化模块的内容形式,也可以异步并行加载。

RequireJS与SeaJS的区别

两者遵循的规范不同,RequireJS遵循AMD规范,SeaJS遵循CMD规范。两者的区别可以参考SeaJS与RequireJS的异同