在学习AMD规范的过程中,顺带学习了CMD规范,随分享如下:
CMD(Common Module Definition),通用模块定义。CMD是SeaJS 在推广过程中对模块定义的规范化产出。因此与AMD类似的,在使用CMD时,也需要引入第三方的库文件 ---- SeaJS。
SeaJS也是主要解决两个问题:
通过上述特点,不难看出CMD与AMD非常相似,只不过在模块定义方式和模块加载时机上两者存在不同。
在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:
define(factory);
define是一个全局函数,用来定义一个模块。接下来会有对参数factory的详细介绍。
另外,与define相关的一个属性cmd。
define.cmd:一个空对象,可用来判定当前页面是否有 CMD 模块加载器。
if (typeof define === "function" && define.cmd) {
// 有 Sea.js 等 CMD 模块加载器存在时
}
factory:可以是一个函数,也可以是一个对象或字符串。
比如可以如下定义一个 JSON 数据模块:
define({"uName":"xdl"});//factory是对象
define('Hello world,my name is {{name}}.'); //factory是字符串
方法在执行时,默认会传入三个参数:require、exports、module
define(function(require, exports, module) {
// 模块代码
});
下面是这三个参数的具体介绍:
============== 参数一:require 参数 =================
例如:
define(function(require,exports,module){
var aM = require('./aM'); // 引入aM模块,这里是相对路径
aM.doSomething(); // 使用aM模块中的方法
});
注意:在开发时,require 的书写要遵循以下规则:
正确拼写。一定要正确拼写require,错误的拼写都是无效的。
不要修改。不能对require进行重新赋值和重命名。
使用直接量。即,require的参数必须是字符串的直接量,不能使用字符串拼接等模式。
与require参数相关的两个方法:async、resolve
i,require.async
require.async(id[, callback?]) 方法的作用是:用来在模块内部异步加载模块,并在加载完成后执行指定回调。一般用来加载可延迟异步加载的模块。
callback 参数,可选。
define(function(require, exports, module) {
// 异步加载一个模块,在加载完成时,执行回调
require.async('./aModule', function(aModule) {
aModule.doSomething();
});
// 异步加载多个模块,在加载完成时,执行回调
require.async(['./bModule', './cModule'], function(bModule, cModule) {
bModule.doSomething();
cModule.doSomething();
});
});
注意:require是同步往下执行;require.async是异步回调执行。
ii,require.resolve
require.resolve(id) 方法的作用是:使用模块系统内部的路径解析机制来解析并返回模块路径。一般用在插件环境或需动态拼接模块路径的场景下。
define(function(require, exports) {
// 获取 bModule 模块的绝对路径
console.log(require.resolve('./bModule')); // ==> http://www.xxx.com/home/path/bModule.js
});
注意:该函数不会加载模块,只返回解析后的绝对路径。
============== 参数二:exports 参数 =================
define(function(require, exports) {
// 对外提供 uName 属性
exports.uName = 'xdl';
// 对外提供 doSomething 方法
exports.doSomething = function() {
console.log('Hello world!');
};
});
另外,还可以通过return直接返回当前模块的接口。
define(function(require) {
// 通过 return 直接提供接口
return {
uName: 'xdl',
doSomething: function() {
console.log('Hello world');
}
};
});
此外,如果return语句是当前模块的唯一代码,以上程序可以简化为:
define({
uName: 'xdl',
doSomething: function() {
console.log('Hello world');
}
});
诶!这不就是个JSONP格式数据模块吗?^_^
本质上:exports 仅仅是 第三个参数 module 的 exports 属性的一个引用。
因此,在 factory 函数内部我们一般不会给这个参数 exports参数 重新赋值。
即使赋值也会无效,甚至出错。例如:
define(function(require, exports) {
// 错误用法!!!
exports = {
uName: 'xdl',
doSomething: function() {
console.log('Hello world');
}
};
});
如果非要给 exports 赋值,可以通过以下形式:
define(function(require, exports, module) {
// 正确用法
module.exports = {
uName: 'xdl',
doSomething: function() {
console.log('Hello world!');
}
}
});
============== 参数三:module 参数 =================
module 的几个常用属性、方法如下:
I. module.id:模块的唯一标识
define('oneModule', [], function(require, exports, module) {
console.log(module.id);// oneModule
// 模块代码
});
上面代码中,oneModule就是当前模块的唯一标识。
注:以上define定义模块的格式是:define(id, deps, factory)。这种用法实际上不属于CMD规范,而属于 Modules/Transport 规范。
II. module.uri:根据模块系统的路径解析规则得到的模块绝对路径。
define(function(require, exports, module) {
console.log(module.uri);// ==> http://www.xxx.com/home/path/this/xxx.js
console.log(module.id);// ==> http://www.xxx.com/home/path/this/xxx.js
});
注:通过 define(factory) 格式创建的模块内部,module.id 和 module.uri 两者完全相同。
III. module.dependencies:是一个数组,表示当前模块的依赖。
IV. module.exports:是一个对象,当前模块对外提供的接口。
传给 factory 构造函数的 exports 参数,是 module.exports 对象的一个引用。通过 exports 参数可以很方便的为当前模块的输出对象添加属性和方法。但是,只通过 exports 参数来提供接口,有时无法满足我们的所有需求,因此 factory 构造函数预留了这个 module 参数,通过修改 module 参数的 exports 属性,能够让我们实现更多的对于输出对象的设置。
例如:修改当前模块对外提供的接口为某一对象的实例时,需要通过 module.exports 来实现:
define(function(require, exports, module) {
// exports 是 module.exports 的一个引用
console.log(module.exports === exports); // true
// 重新给 module.exports 赋值为 XXXCLass 类的一个实例
module.exports = new XXXClass();
// exports 不再等于 module.exports
console.log(module.exports === exports); // false
});
与之前介绍的 AMD 规范相比,CMD 规范在尽量保持简单同时,能够与 CommonJS 和 Node.js 的 Modules 规范保持很强的兼容性。
通过 CMD 规范书写的模块,可以很容易在 Node.js 中运行。
CommonJS规范、AMD规范和CMD规范的区别