首页 > 代码库 > CMD规范学习笔记——基于SEAJS实现

CMD规范学习笔记——基于SEAJS实现

CMD(Common Module Definition):该规范明确了模块的书写格式和基本交互规则。通常一个模块就是一个JS文件。

通过define关键字来定义模块,最基本的格式为:

define(factory);//这里的define是一个全局函数,factory可以是函数或者合法的值。

一、factory为对象:

define({‘foo‘:‘foo‘});//factory为对象,表示该模块的接口为对象。

例子:

html

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>CMD规范的Define函数</title>

<script type="text/javascript" src=http://www.mamicode.com/"../../sea-modules/seajs/seajs/2.2.0/sea.js"></script>

<script type="text/javascript">

   seajs.use("../../static/cmd/main");//使用模块

</script>

</head>

<body>

</body>

</html>

定义模块1:cmdDefine.js

/**

 * define(factory)

 * 模块通过define关键字定义,它是一个全局的方法,factory可以是方法或者是对象和字符串等合法的值

 */

//定义一个模块,该模块的对象接口为{‘foo‘:"foo"}

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

定义模块2:main.js

/**

 * 当factory为函数时,可以接受3个参数,function(require,exports,module)

 */

define(function(require) {

   //通过riquire引用模块

   var cmdDefine=require(‘./cmdDefine‘);

   alert(cmdDefine.foo);//输出对象foo

});

结果:

 技术分享

 

二、factory为字符串

define("This is Test!");

结果:

 技术分享

 

三、factory为数字

define(100);

结果:

 技术分享

 

 

四、当factory函数

cmdDefine.js

/**

 * define(factory)

 * 模块通过define关键字定义,它是一个全局的方法,factory可以是方法或者是对象和字符串等合法的值

 */

//定义一个模块,该模块的对象接口为{‘foo‘:"foo"}

//define({‘foo‘:"foo"});//对象

//define("This is Test!");//字符串

//define(100);//数字

//factory为函数

define(function(require,exports,module){

   //构造函数

   function CmdDefine(){

      alert("This is CmdDefine Constructor!");

   }

   //每个函数都有一个prototype原型对象,可以在原型对象上添加属性和方法,实例化对象的_proto_指向原型对象

   CmdDefine.prototype.say=function(){

      alert("This is CmdDefine say function!");

   }

   module.exports=CmdDefine;//对外发布接口

});

main.js

/**

 * 当factory为函数时,可以接受3个参数,function(require,exports,module)

 */

define(function(require) {

   //通过riquire引用模块

   var CmdDefine=require(‘./cmdDefine‘);

   //alert(cmdDefine.foo);//输出对象foo

   //alert(cmdDefine);//输出字符串This is Test!

   var tmp = new CmdDefine();//创建CmdDefine实例

   tmp.say();//调用say方法

});

结果:

 技术分享

技术分享

 

五、define  也可以接受两个以上参数。字符串  id  表示模块标识,数组deps是模块依赖。带id和deps参数的define用法不属于 CMD 规范,而属于 Modules/Transport 规范。比如:

/**

 * define(factory)

 * 模块通过define关键字定义,它是一个全局的方法,factory可以是方法或者是对象和字符串等合法的值

 */

//定义一个模块,该模块的对象接口为{‘foo‘:"foo"}

//define({‘foo‘:"foo"});//对象

//define("This is Test!");//字符串

//define(100);//数字

//factory为函数

//define(‘cmdDefine‘,[‘jquery‘],function(require,exports,module)

//define接收3个参数,第一个为模块标识,第二个为依赖模块数组

define(‘cmdDefine‘,[‘jquery‘],function(require,exports,module){

   //构造函数

   function CmdDefine(){

      alert("This is CmdDefine Constructor!");

   }

   //每个函数都有一个prototype原型对象,可以在原型对象上添加属性和方法,实例化对象的_proto_指向原型对象

   CmdDefine.prototype.say=function(){

      alert("This is CmdDefine say function!");

   }

   module.exports=CmdDefine;//对外发布接口

});

在seajs中id和deps参数可以省略。省略时,可以通过构建工具自动生成。

六、判断当前页面是否有CMD模块加载器

define.cmd,一个空的对象可以用来判断当前页面是否存在CMD模块加载器。

if(tepyof  define === "function" && define.cmd){

// 有 Sea.js 等 CMD 模块加载器存在

}

七、require:是factory为函数时的第一个参数,也是一个函数,接受一个参数(模块标识ID),用于获取其他模块提供的接口。

/**

 * 当factory为函数时,可以接受3个参数,function(require,exports,module)

 */

define(function(require) {

   //通过riquire获取cmdDefine对外提供的接口

   var CmdDefine=require(‘./cmdDefine‘);

   //alert(cmdDefine.foo);//输出对象foo

   //alert(cmdDefine);//输出字符串This is Test!

   var tmp = new CmdDefine();//创建CmdDefine实例

   tmp.say();//调用say方法

});

八、require.async:是一个方法,接受两个参数(id,callback) ,用于在模块内异步加载模块,并在模块加载完成之后调用回调函数,callback为可选。require是同步往下执行, require.async则是异步回调执行。 require.async 一般用来加载可延迟异步加载的模块。

实际测试中tmp为null,存在问题。

/**

 * 当factory为函数时,可以接受3个参数,function(require,exports,module)

 */

define(function(require) {

   //通过riquire引用模块

   require.async(‘./cmdDefine‘,function(tmp){

      alert("模块加载完成");

     //tmp.say();//调用say方法

   });

});

九、require.resolve:require.resolve(id)

使用模块系统内部的路径解析机制来解析并返回模块路径。该函数不会加载模块,只返回解析后的绝对路径。这可以用来获取模块路径,一般用在插件环境或需动态拼接模块路径的场景下。

/**

 * 当factory为函数时,可以接受3个参数,function(require,exports,module)

 */

define(function(require) {

   //通过riquire引用模块

   var path=require.resolve(‘./cmdDefine‘);

   alert(path);

});

结果:

 技术分享

 

十、exports:Object,是一个对象,用于模块对外提供接口。exports  仅仅是  module.exports  的一个引用。在factory内部exports重新赋值时,并不会改变module.exports的值。因此给  exports赋值是无效的,不能用来更改模块接口。

第一种写法:通过exports添加属性或方法

define(function(require,exports,module){

   exports.foo=‘foo‘;

   exports.print=function(){

      console.log(‘foo‘);

   }

});

第二种写法:直接通过return对外提供接口

define(function(require){

   return {

      foo:‘foo‘,

      print:function(){

         console.log(‘foo‘);

      }

   }

});

第三种写法:如果return语句是模块的唯一代码,可以省略return

define({

   foo : ‘foo‘,

   print : function() {

      console.log(‘foo‘);

   }

});

第四种写法:通过module.exports对外提供接口

/**

 * define(factory)

 * 模块通过define关键字定义,它是一个全局的方法,factory可以是方法或者是对象和字符串等合法的值

 */

define(‘cmdDefine‘,[‘jquery‘],function(require,exports,module){

   //构造函数

   function CmdDefine(){

      alert("This is CmdDefine Constructor!");

   }

   //每个函数都有一个prototype原型对象,可以在原型对象上添加属性和方法,实例化对象的_proto_指向原型对象

   CmdDefine.prototype.say=function(){

      alert("This is CmdDefine say function!");

   }

   module.exports=CmdDefine;//对外发布接口

});

直接给exports赋值是错误的。

/**

 * 不能通过给exports变量赋值对外提供接口,

 * exports只是module.exports的引用,

 * 直接赋值无法改变module.exports的值

 *

 */

define(function(require) {

   exports = {

      foo : ‘foo‘,

      print : function() {

         console.log(‘foo‘);

      }

   }

});

十一:module :Object,是一个对象,它储存与当前模块相关联的属性和方法。

1、module .id:String,模块唯一标识是一个字符串。define函数的第一个参数为模块ID。

/**

 * define(factory)

 * 模块通过define关键字定义,它是一个全局的方法,factory可以是方法或者是对象和字符串等合法的值

 */

define(‘cmdDefine‘,[‘jquery‘],function(require,exports,module){

   //构造函数

   function CmdDefine(){

      alert("This is CmdDefine Constructor!");

   }

   //每个函数都有一个prototype原型对象,可以在原型对象上添加属性和方法,实例化对象的_proto_指向原型对象

   CmdDefine.prototype.say=function(){

      alert("This is CmdDefine say function!");

   }

   module.exports=CmdDefine;//对外发布接口

});

2、module.uri:String: 根据模块系统的路径解析规则得到的模块绝对路径。一般情况下(没有在define中手写id  参数时),module.id的值就是module.uri ,两者完全相同。

define(function(require,exports,module) {

   alert(`"id:"${module.id}`);//模块id

   alert(`"uri:"${module.uri}`);

});

结果:

 技术分享技术分享

 

 

3、module.dependencies:Array:是一个数组,表示当前模块的依赖。

4、module.exports:Object :当前模块对外提供的接口。

传factory构造方法的exports参数是 module.exports 对象的一个引用。只通过 exports 参数来提供接口,有时无法满足开发者的所有需求。比如当模块的接口是某个类的实例时,需要通过  module.exports  来实现:

define(function(require, exports, module) {

  // exports 是 module.exports 的一个引用

  console.log(module.exports === exports); // true

  // 重新给 module.exports 赋值

  module.exports = new SomeClass();

  // exports 不再等于 module.exports

  console.log(module.exports === exports); // false

});

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

//x.js

define(function(require, exports, module) {

  // 错误用法

  setTimeout(function() {

    module.exports = { a: "hello" };

  }, 0);

});

在 y.js 里有调用到上面的 x.js:

//y.js

define(function(require, exports, module) {

  var x = require(‘./x‘);

  // 无法立刻得到模块 x 的属性 a

  console.log(x.a); // undefined

});

 

CMD规范:https://github.com/cmdjs/specification/blob/master/draft/module.md

Common Module Definition / draft

 

 

This specification addresses how modules should be written in order to be interoperable in browser-based environment. By implication, this specification defines the minimum features that a module system must provide in order to support interoperable modules.

?Modules are singletons.

?New free variables within the module scope should not be introduced.

?Execution must be lazy.

 

Module Definition

 

A module is defined with  define  keyword, which is a function.

 

define(factory);

1.The  define  function accepts a single argument, the module factory.

2.The  factory  may be a function or other valid values.

3.If  factory  is a function, the first three parameters of the function, if specified, must be "require", "exports", and "module", in that order.

4.If  factory  is not a function, then the module‘s exports are set to that object.

 

Module Context

 

In a module, there are three free variables:  require ,  exports  and  module .

 

define(function(require, exports, module) {

  // The module code goes here

});

 

The  require  Function

 

1. require  is a function

i. require  accepts a module identifier.

ii. require  returns the exported API of the foreign module.

iii.If requested module cannot be returned,  require  should return null.

 

 

2. require.async  is a function

i. require.async  accepts a list of module identifiers and a optional callback function.

ii.The callback function receives module exports as function arguments, listed in the same order as the order in the first argument.

iii.If requested module cannot be returned, the callback should receive null correspondingly.

 

 

The  exports  Object

 

In a module, there is a free variable called "exports", that is an object that the module may add its API to as it executes.

 

The  module  Object

 

1. module.uri

 

The full resolved uri to the module.

 

 

2. module.dependencies

 

A list of module identifiers that required by the module.

 

 

3. module.exports

 

The exported API of the module. It is the same as  exports  object.

 

 

Module Identifier

1.A module identifier is and must be a literal string.

2.Module identifiers may not have a filename extensions like  .js .

3.Module identifiers should be dash-joined string, such as  foo-bar .

4.Module identifiers can be a relative path, like  ./foo  and  ../bar .

 

Sample Code

 

A typical sample

 

math.js

 

define(function(require, exports, module) {

  exports.add = function() {

    var sum = 0, i = 0, args = arguments, l = args.length;

    while (i < l) {

      sum += args[i++];

    }

    return sum;

  };

});

 

increment.js

 

define(function(require, exports, module) {

  var add = require(‘math‘).add;

  exports.increment = function(val) {

    return add(val, 1);

  };

});

 

program.js

 

define(function(require, exports, module) {

  var inc = require(‘increment‘).increment;

  var a = 1;

  inc(a); // 2

  module.id == "program";

});

 

Wrapped modules with non-function factory

 

object-data.js

 

define({

    foo: "bar"

});

 

array-data.js

 

define([

    ‘foo‘,

    ‘bar‘

]);

 

string-data.js

define(‘foo bar‘);

原文:https://github.com/seajs/seajs/issues/242

CMD规范学习笔记——基于SEAJS实现