CMD规范

在学习AMD规范的过程中,顺带学习了CMD规范,随分享如下:

一,CMD简介

CMD(Common Module Definition),通用模块定义。CMD是SeaJS 在推广过程中对模块定义的规范化产出。因此与AMD类似的,在使用CMD时,也需要引入第三方的库文件 ---- SeaJS。

SeaJS也是主要解决两个问题:

  1. 多个JS文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器。
  2. JS加载的时候,浏览器会停止渲染页面,加载文件越多,浏览器失去响应时间越长。

通过上述特点,不难看出CMD与AMD非常相似,只不过在模块定义方式模块加载时机上两者存在不同。

二,CMD模块的定义

在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:

define(factory);

define是一个全局函数,用来定义一个模块。接下来会有对参数factory的详细介绍。

另外,与define相关的一个属性cmd。

define.cmd:一个空对象,可用来判定当前页面是否有 CMD 模块加载器。

if (typeof define === "function" && define.cmd) {
  // 有 Sea.js 等 CMD 模块加载器存在时
}

三,factory参数

factory:可以是一个函数,也可以是一个对象或字符串。

1,factory 为对象、字符串时:表示模块的接口就是该对象、字符串。

比如可以如下定义一个 JSON 数据模块:

define({"uName":"xdl"});//factory是对象

define('Hello world,my name is {{name}}.');    //factory是字符串

2,factory 为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。

方法在执行时,默认会传入三个参数:require、exports、module

define(function(require, exports, module) {

  // 模块代码

});

下面是这三个参数的具体介绍:

============== 参数一:require 参数 =================

  • require:它是一个方法(函数)。他接收 模块标识 作为唯一参数,用来接受其他模块的接口。

例如:

define(function(require,exports,module){

    var aM = require('./aM');        // 引入aM模块,这里是相对路径

    aM.doSomething();            // 使用aM模块中的方法

});

注意:在开发时,require 的书写要遵循以下规则:

  1. 正确拼写。一定要正确拼写require,错误的拼写都是无效的。

  2. 不要修改。不能对require进行重新赋值和重命名。

  3. 使用直接量。即,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 参数 =================

  • 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:是一个对象,上面存储了与当前模块相关联的一些属性和方法。

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规范的区别

你可能感兴趣的:(编码规范,Web前端,CMD规范)