CMD和seaJS

前面的话

  CMD(Common Module Definition)表示通用模块定义,该规范是国内发展出来的,由阿里的玉伯提出。就像AMD有个requireJS,CMD有个浏览器的实现SeaJS,SeaJS和requireJS同样,都是javascript的模块化解决方案。本文将详细介绍CMD和seaJSjavascript

 

CMD

  在Sea.js中,全部JavaScript模块都遵循CMD(Common Module Definition)模块定义规范。该规范明确了模块的基本书写格式和基本交互规则html

  AMD规范简单到只有一个API,即define函数java

define([module-name?], [array-of-dependencies?], [module-factory-or-object]);

  module-name: 模块标识,能够省略node

  array-of-dependencies: 所依赖的模块,能够省略jquery

  module-factory-or-object: 模块的实现,或者一个JavaScript对象git

  CMD规范也与之相似,只不过第三个参数factory的实现方式不一样。在CMD规范中,一个模块就是一个文件。代码的书写格式以下github

define(id?, deps?, factory)

  与AMD规范相似,define是一个全局函数,用来定义模块。字符串 id 表示模块标识,数组 deps 是模块依赖。这两个参数能够省略,一般由构建工具自动生成数组

  一般地,define()方法的第三个参数factory是一个函数,表示是模块的构造方法。执行该构造方法,能够获得模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:requireexports 和 module浏览器

  [注意]factory()方法的参数若是不须要,能够省略,但不能够修改,如修改成'a'、'b'、'c',也不能够改变其参数的顺序。在函数内部,也不能对参数名从新赋值,如'var a = require; '网络

define(function(require, exports, module) {

  // 模块代码

});

【require】

  require 是 factory 函数的第一个参数。require 是一个方法,接受 模块标识 做为惟一参数,用来获取其余模块提供的接口。通俗地说,经过require()方法来调用其余模块的属性或方法

复制代码
define(function(require, exports, module) {

  // 获取模块 a 的接口
  var a = require('./a');

  // 调用模块 a 的方法
  a.doSomething();

});
复制代码

  这个require()方法的实现和功能都特别相似于CommonJS中的require()方法。或许,有人会有疑惑,require()不是一个同步方法吗?在CommonJS中是的,在seaJS中也能够这么说,但并不完整。更合理的说法应该是,模块内的同步加载,实际表现为对模块a进行预下载

  例以下面的代码,即便不点击页面,a.js也会预先下载。点击页面后,控制台依次输出'a'和'a.test'

复制代码
// main.js
define(function(require, exports, module){
    document.onclick = function(){
        var a = require('js/a');
        a.test();
    }    
});
define(function(require, exports, module){
    console.log('a');
    exports.test = function(){
        console.log('a.test');
    }
})
复制代码

  能不能执行时再下载呢?相似于懒加载。有的,使用require.async()方法。require.async 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调

复制代码
// main.js
define(function(require, exports, module){
    document.onclick = function(){
        require.async('./a',function(a){
            a.test();
        });
    }    
});
//a.js
define(function(require, exports, module){
    console.log('a');
    exports.test = function(){
        console.log('a.test');
    }
})
复制代码

【exports】

  exports 是一个对象,用来向外提供模块接口。与CommonJS的exports功能相似

复制代码
define(function(require, exports) {

  // 对外提供 foo 属性
  exports.foo = 'bar';

  // 对外提供 doSomething 方法
  exports.doSomething = function() {};

});
复制代码

  除了给 exports 对象增长成员,还可使用 return 直接向外提供接口,这种方式与requireJS的方式相似

复制代码
define(function(require) {

  // 经过 return 直接提供接口
  return {
    foo: 'bar',
    doSomething: function() {}
  };

});
复制代码

  若是 return 语句是模块中的惟一代码,还可简化为

define({
  foo: 'bar',
  doSomething: function() {}
});

【module】

  module 是一个对象,上面存储了与当前模块相关联的一些属性和方法

// main.js
define(['./a'],function(require, exports, module){
    console.log(module);
})

  module.uri表示根据模块系统的路径解析规则获得的模块绝对路径

  module.id是模块的惟一标识,通常状况下没有在define中手写id参数时,module.id的值就是module.uri,二者彻底相同

  module.dependencies是一个数组,表示当前模块的依赖

  module.exports是当前模块对外提供的接口。传给factory构造方法的exports参数是module.exports对象的一个引用。只经过exports参数来提供接口,有时没法知足开发者的全部需求。 好比当模块的接口是某个类的实例时,须要经过module.exports来实现

  [注意]对module.exports的赋值须要同步执行,不能放在回调函数里。下面这样是不行的

复制代码
define(function(require, exports, module) {
  // 错误用法
  setTimeout(function() {
    module.exports = { a: "hello" };
  }, 0);
});
复制代码

 

入口

  requireJS经过data-main来设置入口,而seaJS则经过sea.use()来设置。sea.js 在下载完成后,会自动加载入口模块

seajs.use(id, callback?)

  [注意]callback参数可选,省略时,表示无需回调

<script src="sea.js"></script>
<script>
  seajs.use('js/main');
</script>

  加载单个依赖,运行如下代码后,控制台输出'test'

复制代码
//index.html
<script src="sea.js"></script>
<script>
    seajs.config({
        base: 'js'
    });
    seajs.use("main",function(a){
        a.test();
    });
</script>

// main.js
define(['./a'],function(require, exports, module){
    return {
        test : function(){
            console.log('test');
        }
    }
})
复制代码

  加载多个依赖

//并发加载模块 a 和模块 b,并在都加载完成时,执行指定回调
seajs.use(['./a', './b'], function(a, b) {
  a.init();
  b.init();
});

【DOMReady】

  seajs.useDOM ready事件没有任何关系。若是某些操做要确保在DOM ready后执行,须要使用jquery等类库来保证

seajs.use(['jquery', './main'], function($, main) {
  $(document).ready(function() {
    main.init();
  });
});

【打包】

  引入 sea.js 时,能够把 sea.js 与其余文件打包在一块儿,可提早合并好,或利用 combo 服务动态合并。不管哪种方式,为了让 sea.js 内部能快速获取到自身路径,推荐手动加上 id 属性

<script src="path/to/sea.js" id="seajsnode"></script>

  加上 seajsnode 值,可让 sea.js 直接获取到自身路径,而不须要经过其余机制去自动获取。这对性能和稳定性会有必定提高,推荐默认都加上

 

配置

【路径】

  若是不配置路径,在requireJS中,默认路径是data-main的所处目录,好比data-main='js/main',则所处路径是'js'目录下

  而seaJS则不一样,它的默认路径是seaJS文件的所处目录,好比seaJS文件所处路径是'demo'目录下,进行以下入口设置后

seajs.use('js/main');

  说明main.js的目录为'demo/js/main.js'。若是main.js依赖于a.js,且a.js与main.js处于同一目录下,则如下两种写法都正确

  一、'demo' + 'js/a' = 'demo/js/a.js'

// main.js
define(['js/a'],function(require, exports, module){
    
})

  二、'./'表示当前目录,即'demo/js',因此 './a' = 'demo/js/a.js'

// main.js
define(['./a'],function(require, exports, module){
    
})

  在requireJS中使用baseUrl来配置基础路径,而在seaJS中使用base。进行以下配置后,真实路径为 'demo' + 'js' + 'main' = 'demo/js/main.js'

复制代码
<script src="sea.js"></script>
<script>
    seajs.config({
        base: 'js'
    });
    seajs.use("main");
</script>
复制代码

【别名】

  当模块标识很长时,可使用 alias 来简化

复制代码
seajs.config({
  alias: {
    'jquery': 'jquery/jquery/1.10.1/jquery',
    'app/biz': 'http://path/to/app/biz.js',
  }
});
复制代码

【目录】

  当目录比较深,或须要跨目录调用模块时,可使用 paths 来简化书写

复制代码
seajs.config({
  paths: {
    'gallery': 'https://a.alipayobjects.com/gallery',
    'app': 'path/to/app',
  }
});
复制代码

 

与AMD区别

  AMD 是 RequireJS 在推广过程当中对模块定义的规范化产出,CMD 是 SeaJS 在推广过程当中对模块定义的规范化产出。这些规范的实现都能达成浏览器端模块化开发的目的

  AMD与CMD主要有如下两点区别

  一、所依赖模块的执行时机

  对于依赖的模块,AMD是提早执行,CMD是延迟执行

  AMD在加载模块完成后就会执行该模块,全部模块都加载执行完后会进入require的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不必定一致,看网络速度,哪一个先下载下来,哪一个先执行,可是主逻辑必定在全部依赖加载完成后才执行。不过,新版本的RequireJS也能够延迟执行

  CMD加载完某个依赖模块后并不执行,只是下载而已,在全部依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是彻底一致的。若是使用require.async()方法,能够实现模块的懒加载,即不执行不下载

  二、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做为依赖项传递

 

最后

  CommonJSrequireJSseaJS这三种模块化方案,并无高低之分。随着各个方案的不断升级,语言方面相互借鉴,使用差别逐渐变小。以上三种库级别的模块化方案,须要引入额外的库,且所遵循的规范并非标准组织制定的,权威性不足

  随着ES6在语言层面上开始支持模块化,ES6的模块化写法才是将来的模块化标准

相关文章
相关标签/搜索