CMD和AMD探秘

踏上前端这条道路以来,我一直以为自己就是个娴熟的切图工,每天只需要做着重复的劳动,切图,做网站。然而,技术的发展是日新月异的,切图工早就面临淘汰。随着浏览器功能越来越完善,前端项目越来越大,代码越来越复杂,为了适应这种变化,越来越要求前端开发模块化。

首先介绍一下什么是模块化。模块化是指解决某一个复杂问题,按照一种分类的思维把问题进行系统分解,并处理。譬如将巨大的系统代码,整合优化分割成高内聚低耦合的模块,达到便于管理,维护和开发的目的。

模块化设计,必须实现以下能力:

  1. 定义封装的模块
  2. 定义新模块对其他模块的以来
  3. 对其他模块的引入支持。

介绍其中最主要的两种规范,CMD和AMD。

AMD 与 RequireJS

AMD:Asynchronous Module Definition 异步模块定义 [e'sɪŋkrənəs]

AMD采用require([module],callback);加载模块,require有两个参数,一个是依赖的模块数组,一个是回调函数。目前主要有两个Javascript库实现了AMD规范,require.js和curl.js

require.js主要解决以下两个问题:

          (1)实现js文件的异步加载,避免网页失去响应;

          (2)管理模块之间的依赖性,便于代码的编写和维护。

require.js使用方法

          (1)下载js加载入页面

          (2)加载自己的代码    <script src="js/require.js" data-main="js/main"></script>

main.js 

1
require(['moduleA','moduleB'],function(moduleA,moduleB){ 2 //code here 3 });
//假定主模块依赖jquery、underscore和backbone这三个模块,main.js就可以这样写:   
require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){

               // some code here

  });

模块的加载

require.config();    config方法可以对模块的加载行为进行自定义。require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径,baseUrl为基目录

//main.js
 require.config({

            baseUrl:"js/lib",

             paths:{

                    "jquery":"jquery.min",

                    "underscore":"underscore".min,

                    "backbone":"backbone.min"

                   }

          })

AMD模块的写法

模块必须采用特定的define()函数来定义,如果一个模块不依赖于其他模块可以直接定义在define()函数中。

假定一个math模块

  //  math.js

    define(function(){

        var add = function(x,y){

            return x+y;

         };

         return {add:add};

    });

  //  加载方法   main.js

    require(['math'],function(math){

         alert(math.add(1,1));

    });

如果这个模块依赖其他模块,define()函数的第一个参数必须是一个数组,指明该模块的依赖性

define(['myLib'],function(myLib){

    function foo(){

        myLib.func();

    }

     return {foo:foo};

});

//当require()函数加载上面这个模块的时候,就会先加载myLib.js文件。

 require方法调用模块

require(['module1','module2'],function(module1,module2){

     module1.func();

     module2.func();

  })

//在加载 foo 与 bar 两个模块之后执行回调函数实现具体过程。

同时还可以在define定义模块内部进行require调用模块

 

   define( function( require ){
        var m1 = require( 'module1' ),
              m2 = require( 'module2' );
        ...
    });

 

加载非规范的模块

在require()加载之前,先用require.config()方法,定义他们的一些特征。

require.config({

      shim:{

             'underscore':{

                     exports:'_'

                     },

              'backbone':{

                       deps:['underscore','jquery'],

                       exports:'Backbone'
                      }

                   }

});

/*require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义
(1)exports值(输出的变量名),表明这个模块外部调用时的名称;
(2)deps数组,表明该模块的依赖性。
*/ //比如jQuery插件定义 shim:{ 'jquery.scroll':{ deps:['jquery'], exports:'jQuery.fn.scroll' } }

CMD 与 seaJS  (转载)

CMD:Common Module Definition   通用模块加载规范     

在CMS中一个模块就是一个文件,格式和AMD有些类似,define(factory);define定义模块,参数factory可以是一个函数或者对象,字符串,当factory为对象字符串时,表示模块的接口是该对象。

定义JSON数据模块:

define({"foo":"bar"});

通用字符串定义模板模块

 define('this is {{data}}!');

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

 

 define( function(require, exports, module) {
    // 模块代码
 });

 define( id?, deps?, factory );

define也可以接受两个以上的参数,字符串id为模块标识,数组deps为模块依赖:

    define( 'module', ['module1', 'module2'], function( require, exports, module ){
        // 模块代码
    });

 其与 AMD 规范用法不同。

 require 是 factory 的第一个参数。
  require( id );
  接受模块标识作为唯一的参数,用来获取其他模块提供的接口:
    define(function( require, exports ){
        var a = require('./a');
        a.doSomething();
    });

 require.async( id, callback? );

 require是同步往下执行的,需要的异步加载模块可以使用 require.async 来进行加载:

 

    define( function(require, exports, module) {
        require.async('.a', function(a){
            a.doSomething();
        });
    });

exports 是 factory 的第二个参数,用来向外提供模块接口。

    define(function( require, exports ){
        exports.foo = 'bar'; // 向外提供的属性
        exports.do = function(){}; // 向外提供的方法
    });

 当然也可以使用 return 直接向外提供接口。

    define(function( require, exports ){
        return{
            foo : 'bar', // 向外提供的属性
            do : function(){} // 向外提供的方法
        }
    });

也可以简化为直接对象字面量的形式:

    define({
        foo : 'bar', // 向外提供的属性
        do : function(){} // 向外提供的方法
    });

 与nodeJS中一样需要注意的是,一下方式是错误的:

    define(function( require, exports ){
        exports = {
            foo : 'bar', // 向外提供的属性
            do : function(){} // 向外提供的方法
        }
    });

需要这么做

    define(function( require, exports, module ){
        module.exports = {
            foo : 'bar', // 向外提供的属性
            do : function(){} // 向外提供的方法
        }
    });

传 入的对象引用可以添加属性,一旦赋值一个新的对象,那么值钱传递进来的对象引用就会失效了。开始之初,exports 是作为 module.exports 的一个引用存在,一切行为只有在这个引用上 factory 才得以正常运行,赋值新的对象后就会断开引用,exports就只是一个新的对象引用,对于factory来说毫无意义,就会出错。

    module 是factory的第三个参数,为一个对象,上面存储了一些与当前模块相关联的属性与方法。
        module.id 为模块的唯一标识。
        module.uri 根据模块系统的路径解析规则得到模块的绝对路径。
        module.dependencies 表示模块的依赖。
        module.exports 当前模块对外提供的接口。

seaJS

    官网 http://seajs.org/docs/
    sea.js 核心特征:
        1. 遵循CMD规范,与NodeJS般的书写模块代码。
        2. 依赖自动加载,配置清晰简洁。
        兼容 Chrome 3+,Firefox 2+,Safari 3.2+,Opera 10+,IE 5.5+。

 seajs.use  用来在页面中加载一个或者多个模块

    // 加载一个模块
    seajs.use('./a');
    // 加载模块,加载完成时执行回调
    seajs.use('./a',function(a){
        a.doSomething();
    });
    // 加载多个模块执行回调
    seajs.use(['./a','./b'],function(a , b){
        a.doSomething();
        b.doSomething();
    });

AMD 与 CMD 的区别

  下面是玉伯(sea.js创始人)对于 AMD 与 CMD 区别的解释:

    AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
    CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
    类似的还有 CommonJS Modules/2.0 规范,是 BravoJS 在推广过程中对模块定义的规范化产出还有不少??
    这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。
    目前这些规范的实现都能达成浏览器端模块化开发的目的。
    区别:
    1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
    2. CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:
    // CMD
    define(function(require, exports, module) {
        var a = require('./a')
        a.doSomething()
        // 此处略去 100 行
        var b = require('./b') // 依赖可以就近书写
        b.doSomething()
        // ...
    })

    // AMD 默认推荐的是
    define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
        a.doSomething()
        // 此处略去 100 行
        b.doSomething()
        // ...
    })
虽然 AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢上面的写法,也是官方文档里默认的模块定义写法。
    3. AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。
    4. 还有一些细节差异,具体看这个规范的定义就好,就不多说了。
        另外,SeaJS 和 RequireJS 的差异,可以参考:https://github.com/seajs/seajs/issues/277
扩展阅读:
前端模块化开发的价值 https://github.com/seajs/seajs/issues/547
前端模块化开发那点历史 https://github.com/seajs/seajs/issues/588
从 CommonJS 到 Sea.js https://github.com/seajs/seajs/issues/269    

RequireJS和AMD规范  http://javascript.ruanyifeng.com/tool/requirejs.html 

知乎  AMD 和 CMD 的区别有哪 http://www.zhihu.com/question/20351507 

 

posted @ 2016-07-03 11:19  Naomi❤  阅读(361)  评论(1编辑  收藏  举报