CommonJS,AMD模块化规范

[TOC]

模块化开发依然成为前端开发的主流,我们需要做的就是将不同的模块组织起来。最有名的前端模块管理器即requireJS,采用AMD标准。除此之外的其他模块管理器各有特色。

模块化规范

CommonJS

CommonJS用在服务器端,是服务器模块化的一种规范,NodeJS是其具体实现。加载模块是同步的。由于NodeJS主要用于服务器的编程,加载的模块文件一般已经存在本地,所以加载起来很快,不用考虑异步加载问题,而前端浏览器需要从服务器加载模块,则必须异步加载,就需要AMD,CDM。
根据CommonJS规范,一个单独文件就是一个模块,加载模块使用require()方法,该方法读取一个文件并执行,最后返回exports对象。
CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}

  • require()用来引入外部模块;
  • exports对象用于导出当前模块的方法或变量,唯一的导出口;
  • module对象就代表模块本身,存储了模块的元信息。具体如下:
    • module.id——模块的ID。
    • module.dependencies——一个数组,存储了此模块依赖的所有模块的ID列表。
    • module.exports——与exports指向同一个对象。

AMD/CMD

用在浏览器端,异步加载
AMD:异步模块定义,是RequireJS在推广过程中对模块定义的规范化产出。先提出的
CMD:通用模块定义,是SeaJS (由玉伯-淘宝前端工程师提出)在推广过程中对模块定义的规范化产出
区别:

  1. 对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)。
  2. CMD推崇依赖就近,AMD推崇依赖前置。即SeaJS是懒执行,requireJS是预执行。
  • SeaJS只会在真正需要时才执行该模块,严格按照代码中require模块的顺序执行。
  • RequireJS中所有依赖的模块加载被提前并行执行了,执行的顺序不能保证。这个问题使用shim参数就可以解决了,shim里面配置好依赖。

官方的解释:SeaJS与RequireJS 的异同

//AMD 
define(['./a','./b'], function (a, b) { 

    //依赖一开始就写好 
    a.test(); 
    b.test(); 
}); 

//CMD 
define(function (requie, exports, module) { 

    //依赖可以就近书写 
    var a = require('./a'); 
    a.test(); 

    ... 
    //软依赖 
    if (status) { 

        var b = requie('./b'); 
        b.test(); 
    } 
}); 

SeaJS和 RequireJS中的define

define(id?, deps?, factory);

事实上SeaJS和RequireJS的define前两个参数的确一样。id都为字符串,都遵循 Module Identifiers。deps都是指依赖模块,类型都为数组。区别仅在于第三个参数factory,虽然类型也都是函数,但factory的参数意义却不同。
RequireJS中factory的参数有两种情况:

  1. factory实参和deps数组元素一一对应,数组元素有几个,factory实参个数就有几个。
define(['a', 'b'], function(a, b){
        // todo
});
  1. 固定为require,exports, module(modules/wrappings格式)。
define(function(require, exports, module){
    // todo
});

这种方式是RequireJS后期向 Modules/Wrappings 的妥协,即兼容了它。而SeaJS的define仅支持RequireJS的第二种写法:Modules/Wrappings。

SeaJS和 RequireJS中的require

SeaJS中“依赖”都需要使用require函数去获取,虽然SeaJS的define的第二个参数deps也有“依赖”的意思,但它是提供打包工具(SPM)用的。此外,SeaJS的require是作为参数传入匿名函数内的,RequireJS的require则是全局变量。

三种编写模块的模式

    define(function(require, exports, module) {
    var a = require('a'); //引入a模块
    var b = require('b'); //引入b模块

    var data1 = 1; //私有数据

    var func1 = function() { //私有方法
        return a.run(data1);
    }

    exports.data2 = 2; //公共数据

    exports.func2 = function() { //公共方法
        return 'hello';
    }
});

或者:

    define(function(require) {
    var a = require('a'); //引入a模块
    var b = require('b'); //引入b模块

    var data1 = 1; //私有数据

    var func1 = function() { //私有方法
        return a.run(data1);
    }

    return {
        data2: 2,
        func2: function() {
            return 'hello';
        }
    };
});

如果模块定义没有其它代码,只返回一个对象,还可以有如下简化写法:

define({
    data: 1,
    func: function() {
        return 'hello';
    }
})

这种写法适合定义纯JSON数据的模块。

模块化管理器

bower

主要用来进行模块的安装,升级和删除

npm install -g bower
bower install jquery
bower update jquery
bower uninstall jquery

具体内容详见bower一文中。

Grunt

前端开发利器,用来打包管理.js
具体详见grunt+bower 一文中。

browserify

Browserify 是目前最常用的 CommonJS 格式转换的工具。本身也是一个NodeJS模块,可以通过npm来安装。使用类似NodeJS的require的方式来组织浏览器的javascript端代码。

npm install -g browserify
文章目录
  1. 1. 模块化规范
    1. 1.1. CommonJS
    2. 1.2. AMD/CMD
      1. 1.2.1. SeaJS和 RequireJS中的define
      2. 1.2.2. SeaJS和 RequireJS中的require
      3. 1.2.3. 三种编写模块的模式
  2. 2. 模块化管理器
    1. 2.1. bower
    2. 2.2. Grunt
    3. 2.3. browserify