前端规范

AMD与CMD

AMD:异步模块定义
ref: https://github.com/amdjs/amdjs-api/wiki/AMD

CMD: 通用模块定义
ref: https://github.com/seajs/seajs/issues/242

这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。目前这些规范的实现都能达成浏览器端模块化开发的目的。

区别:

  1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。
  2. CMD 推崇依赖就近,AMD 推崇依赖前置。

ref: https://github.com/seajs/seajs/issues/277

说下我自己的看法:

  1. CMD推崇依赖就近,可以把依赖写进你的代码中的任意一行, 如

    define(function(require, exports, module) {
      var a = require('./a')
      a.doSomething()
      var b = require('./b')
      b.doSomething()
    });
    
  2. AMD是依赖前置的,换句话说,在解析和执行当前模块之前,模块作者必须指明当前模块所依赖的模块。如

    define(['./a','./b'],function(a,b) {
       a.doSomething()
       b.doSomething()
    }) ;
    

代码在一旦运行到此处,能立即知晓依赖。而无需遍历整个函数体找到它的依赖,因此性能有所提升,缺点就是开发者必须显式得指明依赖——这会使得开发工作量变大,比如:当你写到函数体内部几百上千行的时候,忽然发现需要增加一个依赖,你不得不回到函数顶端来将这个依赖添加进数组。

  1. 依赖分为硬依赖软依赖, 如:

    if(status){
      a.doSomething()
    }
    

此时,在if体中,可能依赖a,也可能不依赖a,这种可以称之为软依赖

对于软依赖的处理,我推荐依赖前置+回调函数的实现形式。如:

<!-- lang: js -->
if(status){
  async(['a'],function(a){
    a.doSomething()
  })
}
  1. 对于强依赖,如果要性能优先,则考虑参照依赖前置的思想设计你的模块加载器,我个人也更推崇这个方案一些;如果考虑开发成本优先,则考虑按照依赖就近的思想设计你的模块加载器。
    对于弱依赖,只需要将弱依赖的部分改写到回调函数内即可。

CommonJS规范

CommonJS是服务器端模块的规范,Node.js采用了这个规范。

根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。如:

<!-- lang: js -->
console.log("evaluating example.js");

var invisible = function () {
  console.log("invisible");
}

exports.message = "hi";

exports.say = function () {
  console.log(message);
}

使用require方法,加载example.js。

<!-- lang: js -->
var example = require('./example.js');

这时,变量example就对应模块中的exports对象,于是就可以通过这个变量,使用模块提供的各个方法。

<!-- lang: js -->
{
  message: "hi",
  say: [Function]
}

require方法默认读取js文件,所以可以省略js后缀名。

<!-- lang: js -->
var example = require('./example');

js文件名前面需要加上路径,可以是相对路径(相对于使用require方法的文件),也可以是绝对路径。如果省略路径,node.js会认为,你要加载一个核心模块,或者已经安装在本地 node_modules 目录中的模块。如果加载的是一个目录,node.js会首先寻找该目录中的 package.json 文件,加载该文件 main 属性提到的模块,否则就寻找该目录下的 index.js 文件。

下面的例子是使用一行语句,定义一个最简单的模块。

<!-- lang: js -->
// addition.js

exports.do = function(a, b){ return a + b };

上面的语句定义了一个加法模块,做法就是在exports对象上定义一个do方法,那就是供外部调用的方法。使用的时候,只要用require函数调用即可。

<!-- lang: js -->
var add = require('./addition');

add.do(1,2)
// 3

再看一个复杂一点的例子。

<!-- lang: js -->
// foobar.js

function foobar(){
        this.foo = function(){
                console.log('Hello foo');
        }

        this.bar = function(){
                console.log('Hello bar');
        }
}

exports.foobar = foobar;

调用该模块的方法如下:

<!-- lang: js -->
var foobar = require('./foobar').foobar,
test   = new foobar();

test.bar(); // 'Hello bar'

有时,不需要exports返回一个对象,只需要它返回一个函数。这时,就要写成module.exports。

<!-- lang: js -->
module.exports = function () {
  console.log("hello world")
}

AMD规范与CommonJS规范的兼容性

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。

AMD规范使用define方法定义模块,下面就是一个例子:

<!-- lang: js -->
define(['package/lib'], function(lib){

    function foo(){
        lib.log('hello world!');
    } 

    return {
        foo: foo
    };
});

AMD规范允许输出的模块兼容CommonJS规范,这时define方法需要写成下面这样:

<!-- lang: js -->
define(function( require, exports, module )
    var someModule = require( "someModule" );
    var anotherModule = require( "anotherModule" );    

    someModule.doTehAwesome();
    anotherModule.doMoarAwesome();

    exports.asplode = function() {
        someModule.doTehAwesome();
        anotherModule.doMoarAwesome();
    };
});

参考:

  1. http://www.zhihu.com/question/20351507
  2. http://www.zhihu.com/question/21347409#answer-2323656
  3. http://javascript.ruanyifeng.com/nodejs/commonjs.html

你可能感兴趣的:(前端规范)