浅析JS模块规范:AMD和CMD
AMD
,CMD
规范前,我们先来简单地了解下什么是模块?CMD
和AMD
。AMD
,异步模块定义(Asynchronous Module Definition),它是依赖前置 (依赖必须一开始就写好)会先尽早地执行(依赖)模块 。换句话说,所有的require
都被提前执行(require 可以是全局或局部 )。define
,它是全局变量。用法:defind(id, dependencies, factory)
id
:字符串类型,指定义中模块的名称,可选。如果没有提供该参数,模块的名称应该默认为模块加载器请求的指定脚本的名称。如果提供了该参数,模块名必须是“顶级”的和绝对的(不允许相对名称)。dependencies
:array类型,包含一组当前模块依赖的,已被模块定义的模块标识。
["require", "exports", "module"]
。然而,如果工厂方法的长度属性小于3,加载器会选择以函数的长度属性指定的参数个数调用工厂方法。factory
:函数(工厂方法),模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值。
模块名是用正斜杠分割的有意义单词的字符串
单词须为驼峰形式,或者".",".."
模块名不允许文件扩展名的形式,如“.js”
模块名可以为 "相对的" 或 "顶级的"。如果首字符为“.”或“..”则为相对的模块名
顶级的模块名从根命名空间的概念模块解析
相对的模块名从 "require" 书写和调用的模块解析
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AMD</title>
</head>
<body>
<script data-main="scripts/main" src="require.js"></script>
</body>
</html>
data-main
属性指定入口文件为scripts/main(这里省略后缀.js)。/*
* a.js
* 创建一个名为“a”的模块
*/
define('a', function(require, exports, module) {
exports.getTime = function() {
return new Date();
}
});
/*
* b.js
* 创建一个名为“b”的模块,同时使用依赖require、exports和名为“a”的模块:
*/
define('b', ['require', 'exports', 'a'], function(require, exports, a) { exports.test = function() {
return {
now: a.getTime()
};
}
});
/* main.js */
require(['b'], function(b) {
console.log(b.test());
});
CMD
(Common Module Definition)更贴近 CommonJS Modules/1.1 和 Node Modules 规范,一个模块就是一个文件;它推崇依赖就近,想什么时候 require
就什么时候加载,实现了懒加载(延迟执行 ) ;它也没有全局 require, 每个API都简单纯粹 。CMD
规范中,一个模块就是一个文件。代码的书写格式如下:define(factory);
require、exports 和 module
:define(function(require, exports, module) {
// 模块代码
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CMD</title>
</head>
<body>
<script src="sea.js"></script>
<script>
/* 加载入口模块 */
seajs.use('./scripts/main');
</script>
</body>
</html>
/*
* a.js
* 一个文件就是一个模块
*/
define(function(require, exports, module) {
exports.getTime = function() {
return new Date();
}
});
/* main.js */
define(function(require, exports, module) {
/* 按需加载a.js */
var a = require('./a');
console.log(a.getTime());
});
Sea.js
的共同点:- RequireJS 和 Sea.js 都是模块加载器
- 定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。Sea.js 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 环境中。
- 遵循的规范不同。RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD (通用模块定义)规范。规范的不同,导致了两者 API 不同。Sea.js 更贴近 CommonJS Modules/1.1 和 Node Modules 规范。
- 推广理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。Sea.js 不强推,采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
- 对开发调试的支持有差异。Sea.js 非常关注代码的开发调试,有 nocache、debug 等用于调试的插件。RequireJS 无这方面的明显支持。
- 插件机制不同。RequireJS 采取的是在源码中预留接口的形式,插件类型比较单一。Sea.js 采取的是通用事件机制,插件类型更丰富。
AMD
:依赖前置,预执行(异步加载:依赖先执行)。CMD
:依赖就近,懒(延迟)执行(运行到需加载,根据顺序执行)
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething();
// 省略1万行
var b = require('./b') // 依赖可以就近书写
b.doSomething();
})
// AMD 默认推荐的是
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething();
// 省略1万行
b.doSomething();
})