New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMD 模块定义规范 #242

Closed
lifesinger opened this Issue Jul 2, 2012 · 63 comments

Comments

Projects
None yet
@lifesinger
Member

lifesinger commented Jul 2, 2012

CMD 模块定义规范

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

在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:

define(factory);

define Function

define 是一个全局函数,用来定义模块。

define define(factory)

define 接受 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串。

factory 为对象、字符串时,表示模块的接口就是该对象、字符串。比如可以如下定义一个 JSON 数据模块:

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

也可以通过字符串定义模板模块:

define('I am a template. My name is {{name}}.');

factory 为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:requireexportsmodule

define(function(require, exports, module) {

  // 模块代码

});

define define(id?, deps?, factory)

define 也可以接受两个以上参数。字符串 id 表示模块标识,数组 deps 是模块依赖。比如:

define('hello', ['jquery'], function(require, exports, module) {

  // 模块代码

});

iddeps 参数可以省略。省略时,可以通过构建工具自动生成。

注意:带 iddeps 参数的 define 用法不属于 CMD 规范,而属于 Modules/Transport 规范。

define.cmd Object

一个空对象,可用来判定当前页面是否有 CMD 模块加载器:

if (typeof define === "function" && define.cmd) {
  // 有 Sea.js 等 CMD 模块加载器存在
}

require Function

requirefactory 函数的第一个参数。

require require(id)

require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口。

define(function(require, exports) {

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

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

});

注意:在开发时,require 的书写需要遵循一些 简单约定

require.async require.async(id, callback?)

require.async 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调。callback 参数可选。

define(function(require, exports, module) {

  // 异步加载一个模块,在加载完成时,执行回调
  require.async('./b', function(b) {
    b.doSomething();
  });

  // 异步加载多个模块,在加载完成时,执行回调
  require.async(['./c', './d'], function(c, d) {
    c.doSomething();
    d.doSomething();
  });

});

注意require 是同步往下执行,require.async 则是异步回调执行。require.async 一般用来加载可延迟异步加载的模块。

require.resolve require.resolve(id)

使用模块系统内部的路径解析机制来解析并返回模块路径。该函数不会加载模块,只返回解析后的绝对路径。

define(function(require, exports) {

  console.log(require.resolve('./b'));
  // ==> http://example.com/path/to/b.js

});

这可以用来获取模块路径,一般用在插件环境或需动态拼接模块路径的场景下。

exports Object

exports 是一个对象,用来向外提供模块接口。

define(function(require, exports) {

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

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

});

除了给 exports 对象增加成员,还可以使用 return 直接向外提供接口。

define(function(require) {

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

});

如果 return 语句是模块中的唯一代码,还可简化为:

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

上面这种格式特别适合定义 JSONP 模块。

特别注意:下面这种写法是错误的!

define(function(require, exports) {

  // 错误用法!!!
  exports = {
    foo: 'bar',
    doSomething: function() {}
  };

});

正确的写法是用 return 或者给 module.exports 赋值:

define(function(require, exports, module) {

  // 正确写法
  module.exports = {
    foo: 'bar',
    doSomething: function() {}
  };

});

提示exports 仅仅是 module.exports 的一个引用。在 factory 内部给 exports 重新赋值时,并不会改变 module.exports 的值。因此给 exports 赋值是无效的,不能用来更改模块接口。

module Object

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

module.id String

模块的唯一标识。

define('id', [], function(require, exports, module) {

  // 模块代码

});

上面代码中,define 的第一个参数就是模块标识。

module.uri String

根据模块系统的路径解析规则得到的模块绝对路径。

define(function(require, exports, module) {

  console.log(module.uri); 
  // ==> http://example.com/path/to/this/file.js

});

一般情况下(没有在 define 中手写 id 参数时),module.id 的值就是 module.uri,两者完全相同。

module.dependencies Array

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

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 模块定义规范的所有内容。经常使用的 API 只有 define, require, require.async, exports, module.exports 这五个。其他 API 有个印象就好,在需要时再来查文档,不用刻意去记。

与 RequireJS 的 AMD 规范相比,CMD 规范尽量保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。通过 CMD 规范书写的模块,可以很容易在 Node.js 中运行,后续会介绍。

祝使用愉快,有任何想法建议,欢迎反馈留言。

@lifesinger

This comment has been minimized.

Show comment
Hide comment
@lifesinger

lifesinger Jul 6, 2012

Member

有任何问题,欢迎留言交流。
注意:已解决的问题,会在整理后删除掉。

Member

lifesinger commented Jul 6, 2012

有任何问题,欢迎留言交流。
注意:已解决的问题,会在整理后删除掉。

@hanyangecho

This comment has been minimized.

Show comment
Hide comment
@hanyangecho

hanyangecho Jul 2, 2013

请问module.id主要做什么用?用于那些场景?

hanyangecho commented Jul 2, 2013

请问module.id主要做什么用?用于那些场景?

@lifesinger

This comment has been minimized.

Show comment
Hide comment
@lifesinger

lifesinger Jul 2, 2013

Member

@hanyangecho 有些情况下,我们书写模块时,会手写 id:

define('xxx', fn)

module.id 用来存储原始的 xxx 值。一般情况下没有用,在写插件时,有时会用到。id 是模块信息的一部分,保持完备性。

Member

lifesinger commented Jul 2, 2013

@hanyangecho 有些情况下,我们书写模块时,会手写 id:

define('xxx', fn)

module.id 用来存储原始的 xxx 值。一般情况下没有用,在写插件时,有时会用到。id 是模块信息的一部分,保持完备性。

@fjarcticfox

This comment has been minimized.

Show comment
Hide comment
@fjarcticfox

fjarcticfox Aug 5, 2013

想知道seajs维护的持续性怎么样?是否会有团队长期维护?

fjarcticfox commented Aug 5, 2013

想知道seajs维护的持续性怎么样?是否会有团队长期维护?

@afc163

This comment has been minimized.

Show comment
Hide comment
@afc163

afc163 Aug 5, 2013

Member

@fjarcticfox 这个你翻翻最近的 commit 和 issues 大概就能知道这个社区的活跃程度了。

Member

afc163 commented Aug 5, 2013

@fjarcticfox 这个你翻翻最近的 commit 和 issues 大概就能知道这个社区的活跃程度了。

@xuexb

This comment has been minimized.

Show comment
Hide comment
@xuexb

xuexb Aug 9, 2013

seajs里的define.cmd不是undefined吗? 如果用它判断就挂了. seajs 2.0里

xuexb commented Aug 9, 2013

seajs里的define.cmd不是undefined吗? 如果用它判断就挂了. seajs 2.0里

@afc163

This comment has been minimized.

Show comment
Hide comment
@afc163

afc163 Aug 9, 2013

Member

@xuexb 2.1 加回来了。

Member

afc163 commented Aug 9, 2013

@xuexb 2.1 加回来了。

@xuexb

This comment has been minimized.

Show comment
Hide comment
@xuexb

xuexb Aug 22, 2013

模块依赖问题
//xl.js
define("xl",function () {
//code
return {
name:"is name",
age:"24"
}
});

//home.js
define("home",["xl"],function(){
//这里依赖模块xl,但怎么用模块xl呢?因为模块xl没有全局对象,普通的时候地用var xl=require("xl");而使用依赖应该怎么用呢
});

xuexb commented Aug 22, 2013

模块依赖问题
//xl.js
define("xl",function () {
//code
return {
name:"is name",
age:"24"
}
});

//home.js
define("home",["xl"],function(){
//这里依赖模块xl,但怎么用模块xl呢?因为模块xl没有全局对象,普通的时候地用var xl=require("xl");而使用依赖应该怎么用呢
});

@afc163

This comment has been minimized.

Show comment
Hide comment
@afc163

afc163 Aug 22, 2013

Member

@xuexb

var xl=require("xl") 对,就这么用。

Member

afc163 commented Aug 22, 2013

@xuexb

var xl=require("xl") 对,就这么用。

@xuexb

This comment has been minimized.

Show comment
Hide comment
@xuexb

xuexb Aug 29, 2013

那加不加模块依赖都得用require来引用, 那依赖表示?

xuexb commented Aug 29, 2013

那加不加模块依赖都得用require来引用, 那依赖表示?

@lifesinger

This comment has been minimized.

Show comment
Hide comment
@lifesinger

lifesinger Aug 29, 2013

Member

var xl = require('xl') 就这么用啊

Member

lifesinger commented Aug 29, 2013

var xl = require('xl') 就这么用啊

@xuexb

This comment has been minimized.

Show comment
Hide comment
@xuexb

xuexb Sep 5, 2013

问个疑惑哦:
每个看着没事, 但连起来就感觉怪怪的.

以下为引用

  • Sea.js 中,所有 JavaScript 模块都遵循 CMD(Common Module Definition) 模块定义规范。
  • define 也可以接受两个以上参数。字符串 id 表示模块标识,数组 deps 是模块依赖。
  • 注意:带 id 和 deps 参数的 define 用法不属于 CMD 规范,而属于 Modules/Transport 规范。
  • iddeps 参数可以省略。省略时,可以通过构建工具自动生成

上述表明, id,deps可省略,而工具会自动生成. 又提到带他们的不是CMD规范...会有种误会的感觉

xuexb commented Sep 5, 2013

问个疑惑哦:
每个看着没事, 但连起来就感觉怪怪的.

以下为引用

  • Sea.js 中,所有 JavaScript 模块都遵循 CMD(Common Module Definition) 模块定义规范。
  • define 也可以接受两个以上参数。字符串 id 表示模块标识,数组 deps 是模块依赖。
  • 注意:带 id 和 deps 参数的 define 用法不属于 CMD 规范,而属于 Modules/Transport 规范。
  • iddeps 参数可以省略。省略时,可以通过构建工具自动生成

上述表明, id,deps可省略,而工具会自动生成. 又提到带他们的不是CMD规范...会有种误会的感觉

@beijingme

This comment has been minimized.

Show comment
Hide comment
@beijingme

beijingme Sep 16, 2013

麻烦帮忙看下我写的哪里有问题,拜托!

问题1:jquery无法加载

seajs.config({
        paths: {
            'static': 'http://192.168.2.141:96/js/seajs'    
        },
        alias:{
            'jq': 'static/global/jquery-1.9.1.js'   
        }   
    });
seajs.use('static/test/main');

//main.js
define(function (require, exports, module) {
    var $ = require('jq'); //为什么是null
    var Preson = require('./preson');
    var p1 = new Preson('jack', 31);
    p1.say();
});

问题2:这种写法 Preson对象是null

define('static/test/main', ['jq'], function (require, exports, module) {
    var $ = require('jq');
    var Preson = require('./preson');
    var p1 = new Preson('jack', 31);
    p1.say();
});

beijingme commented Sep 16, 2013

麻烦帮忙看下我写的哪里有问题,拜托!

问题1:jquery无法加载

seajs.config({
        paths: {
            'static': 'http://192.168.2.141:96/js/seajs'    
        },
        alias:{
            'jq': 'static/global/jquery-1.9.1.js'   
        }   
    });
seajs.use('static/test/main');

//main.js
define(function (require, exports, module) {
    var $ = require('jq'); //为什么是null
    var Preson = require('./preson');
    var p1 = new Preson('jack', 31);
    p1.say();
});

问题2:这种写法 Preson对象是null

define('static/test/main', ['jq'], function (require, exports, module) {
    var $ = require('jq');
    var Preson = require('./preson');
    var p1 = new Preson('jack', 31);
    p1.say();
});
@lifesinger

This comment has been minimized.

Show comment
Hide comment
@lifesinger

lifesinger Sep 16, 2013

Member

@beijingme

问题1应该是 jquery-1.9.1.js 中 define 的 id 不对,参考 #930

问题2是你写错了,依赖应该是 ['jq', './preson']

Member

lifesinger commented Sep 16, 2013

@beijingme

问题1应该是 jquery-1.9.1.js 中 define 的 id 不对,参考 #930

问题2是你写错了,依赖应该是 ['jq', './preson']

@hkongm

This comment has been minimized.

Show comment
Hide comment
@hkongm

hkongm Sep 16, 2013

用第一种写法吧,看看networks里有没有jq.js的请求。
另外,应该是person,不是preson XD

hkongm commented Sep 16, 2013

用第一种写法吧,看看networks里有没有jq.js的请求。
另外,应该是person,不是preson XD

@beijingme

This comment has been minimized.

Show comment
Hide comment
@beijingme

beijingme Sep 16, 2013

@lifesinger
谢谢,第一问题我通过使用处理好的jquery已经好了,第二个问题,是我太马虎,也感谢hkongm

beijingme commented Sep 16, 2013

@lifesinger
谢谢,第一问题我通过使用处理好的jquery已经好了,第二个问题,是我太马虎,也感谢hkongm

@hite

This comment has been minimized.

Show comment
Hide comment
@hite

hite Dec 6, 2013

语法错误,有点强迫症~。。举例的代码里doSomething属性的值后面 多了分号。

// 正确写法
  module.exports = {
    foo: 'bar',
    doSomething: function() {};
  };

hite commented Dec 6, 2013

语法错误,有点强迫症~。。举例的代码里doSomething属性的值后面 多了分号。

// 正确写法
  module.exports = {
    foo: 'bar',
    doSomething: function() {};
  };
@selfiprvm

This comment has been minimized.

Show comment
Hide comment
@selfiprvm

selfiprvm Oct 6, 2015

说句废话,学些了

selfiprvm commented Oct 6, 2015

说句废话,学些了

@SansaZhang

This comment has been minimized.

Show comment
Hide comment
@SansaZhang

SansaZhang Feb 17, 2016

您好,define的第二个参数deps自动生成的时候的是所有require()引用的模块组成的数组吗?他们会自动合并吗?显示的写出来的话有好处吗?

SansaZhang commented Feb 17, 2016

您好,define的第二个参数deps自动生成的时候的是所有require()引用的模块组成的数组吗?他们会自动合并吗?显示的写出来的话有好处吗?

@huhu213

This comment has been minimized.

Show comment
Hide comment
@huhu213

huhu213 May 11, 2016

“exports 仅仅是 module.exports 的一个引用。在 factory 内部给 exports 重新赋值时,并不会改变 module.exports 的值。因此给 exports 赋值是无效的,不能用来更改模块接口。”

这里说exports是module.exports的引用是不是不太准确啊,如果是引用的话,exports指向的也是module.exports指向的内存区域啊?

小白提问求轻拍╭(╯^╰)╮

huhu213 commented May 11, 2016

“exports 仅仅是 module.exports 的一个引用。在 factory 内部给 exports 重新赋值时,并不会改变 module.exports 的值。因此给 exports 赋值是无效的,不能用来更改模块接口。”

这里说exports是module.exports的引用是不是不太准确啊,如果是引用的话,exports指向的也是module.exports指向的内存区域啊?

小白提问求轻拍╭(╯^╰)╮

@baiduoduo

This comment has been minimized.

Show comment
Hide comment
@baiduoduo

baiduoduo Aug 14, 2016

我用grunt-cmd-concat合并a.js,b.js,c.js时,出现这个错误:
Warning: Task "jshint:build" failed. Use --force to continue.
Aborted due to warnings.

我的配置是这样的:
concat:{
options:{
separator:';'
stripBanners: true,
banner: '/!合并后的文件my_seaJS.js - - 2016-8-14 */'
},
dist:{
src:'js/
.js',
dest:'dist/my_seaJS.js'
}
}
请问这是什么原因呢?

baiduoduo commented Aug 14, 2016

我用grunt-cmd-concat合并a.js,b.js,c.js时,出现这个错误:
Warning: Task "jshint:build" failed. Use --force to continue.
Aborted due to warnings.

我的配置是这样的:
concat:{
options:{
separator:';'
stripBanners: true,
banner: '/!合并后的文件my_seaJS.js - - 2016-8-14 */'
},
dist:{
src:'js/
.js',
dest:'dist/my_seaJS.js'
}
}
请问这是什么原因呢?

@IceHugh

This comment has been minimized.

Show comment
Hide comment
@IceHugh

IceHugh Aug 17, 2016

看这一段,dist src js下面所有js文件应该是*.js,但是他报的错误是你的jshint差价配置问题
Sent from my iPhone

On Aug 14, 2016, at 11:34, 陌上 notifications@github.com wrote:

我用grunt-cmd-concat合并a.js,b.js,c.js时,出现这个错误:
Warning: Task "jshint:build" failed. Use --force to continue.
Aborted due to warnings.

我的配置是这样的:
concat:{
options:{
separator:';'
stripBanners: true,

banner: '/!合并后的文件my_seaJS.js - - 2016-8-14 */'

},
dist:{
src:'js/.js',
dest:'dist/my_seaJS.js'
}
}
请问这是什么原因呢?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

IceHugh commented Aug 17, 2016

看这一段,dist src js下面所有js文件应该是*.js,但是他报的错误是你的jshint差价配置问题
Sent from my iPhone

On Aug 14, 2016, at 11:34, 陌上 notifications@github.com wrote:

我用grunt-cmd-concat合并a.js,b.js,c.js时,出现这个错误:
Warning: Task "jshint:build" failed. Use --force to continue.
Aborted due to warnings.

我的配置是这样的:
concat:{
options:{
separator:';'
stripBanners: true,

banner: '/!合并后的文件my_seaJS.js - - 2016-8-14 */'

},
dist:{
src:'js/.js',
dest:'dist/my_seaJS.js'
}
}
请问这是什么原因呢?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

@zhanglei923

This comment has been minimized.

Show comment
Hide comment
@zhanglei923

zhanglei923 Aug 27, 2016

这个cmd和玉伯真是一本正经的胡说八道!弄的这个破烂玩意,强词夺理,什么狗屁东西!

zhanglei923 commented Aug 27, 2016

这个cmd和玉伯真是一本正经的胡说八道!弄的这个破烂玩意,强词夺理,什么狗屁东西!

@apawn

This comment has been minimized.

Show comment
Hide comment
@apawn

apawn Sep 14, 2016

关于很多人说 jQuery 无法通过require加载的问题,我做一个回答
因为 cmd 是国内定义的,所以jQuery是不承认的,在jQuery的源代码里并没有define seajs 模块,所以我们需要手动在jQuery源代码中加入

if(typeof require === "function" && define.cmd){
   define(function(require){
      return jQuery ;
   })
}

// 通过上面这种方式就可以了。

apawn commented Sep 14, 2016

关于很多人说 jQuery 无法通过require加载的问题,我做一个回答
因为 cmd 是国内定义的,所以jQuery是不承认的,在jQuery的源代码里并没有define seajs 模块,所以我们需要手动在jQuery源代码中加入

if(typeof require === "function" && define.cmd){
   define(function(require){
      return jQuery ;
   })
}

// 通过上面这种方式就可以了。

@Yongest

This comment has been minimized.

Show comment
Hide comment
@Yongest

Yongest Dec 7, 2017

require.js 可以异步加载js,sea.js可以吗

Yongest commented Dec 7, 2017

require.js 可以异步加载js,sea.js可以吗

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment