seajs / seajs

A Module Loader for the Web

Home Page:https://seajs.github.io/seajs/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CMD 模块定义规范

lifesinger opened this issue · comments

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 中运行,后续会介绍。

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

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

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

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

define('xxx', fn)

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

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

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

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

@xuexb 2.1 加回来了。

模块依赖问题
//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

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

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

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

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

以下为引用

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

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

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

问题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

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

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

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

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

commented

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

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

@hite 感谢指正,已修改。

require.async异步都不能保证模块加载成功才回调?是否逻辑应该改变成模块加载成功时才回调呢?假设情景是我需要jquery才能使用,但是它却不管加载成功与否(其实只是保证加载运行结束触发了onload)就进行回调,那么,这样,我就得自己保证 if (!$) throw new Error('没有jquery?'),

@qidizi 空说无凭,给 demo

@qidizi 一般都会先运完JS文件后才触发onload么? 应该是这样

@afc163
t.htm

<script src="https://a.alipayobjects.com/seajs/seajs/2.1.1/sea.js" id="seajsnode"><!--使用阿里的cdn--></script>
<script>
seajs.config({

  // 别名配置
  alias: {
    'jquery': 'AraleCDN/jquery/jquery/1.10.1/jquery',
    'jquery2': 'AraleCDN/jquery/jquery/1.10.1/jquery-not-exist.js#',
    'cookie':'AraleCDN/arale/cookie/1.0.2/cookie'
  },

  // 路径配置
  paths: {
    'AraleCDN': 'https://a.alipayobjects.com',
    'root':'./'
    },

  // 调试模式
  debug: true,
  // 文件编码
  charset: 'utf-8'
});
//主入口
seajs.use('root/d');
</script>

d.js

// 所有模块都通过 define 来定义
define(function(require, exports, module) {
    var $ = require('jquery');
    alert($);
    require.async('jquery2',function($){alert($);});
});

第一个弹出正确,
第二个弹出null,因为jqueyr不存在,但是还是加载成功了,至于像ie6之类没有onerror事件,可能实现这个有点麻烦,很难知道js是加载成功了还是加载失败了,反正还是会触发加载完成的事件

@qidizi 可以看看 #453 #921

所以说,最终模块加载成功与否还是得自己来做保证.它只负责加载步骤,成功与否是自己来保证,我意思就是说是否机制能保证,看来不行,就得逻辑代码上来保证这个依赖性了.

逻辑上自己保证

2014-03-10 11:09 GMT+08:00 qidizi notifications@github.com:

所以说,最终模块加载成功与否还是得自己来做保证.它只负责加载步骤,成功与否是自己来保证,我意思就是说是否机制能保证,看来不行,就得逻辑代码上来保证这个依赖性了.


Reply to this email directly or view it on GitHubhttps://github.com//issues/242#issuecomment-37150219
.

王保平 / 玉伯(射雕)
送人玫瑰手有余香

我能否简单理解,所谓CMD和AMD就是提供一定的基础函数(从面向对象来说:就是提供一定的接口)

提供了一种语法。

require.async(id, callback?)

是否会返回一个Promise对象,让我的回调平行化,我看没有返回,我不想嵌套回调该怎么做?

commented

Promise要加就太大了

没必要,倒是以后可以考虑返回 generator,不过这个还是交给第三方封装。

On Fri, May 30, 2014 at 10:55 AM, army8735 notifications@github.com wrote:

Promise要加就太大了


Reply to this email directly or view it on GitHub
#242 (comment).

王保平 / 玉伯(射雕)
送人玫瑰手有余香

commented

看了还几天了,还是有点晕

commented

这个论坛的形式也有点晕。还不会用呢。

define(['jquery'], function(require, exports, module) {
  var $ = require("jquery");
  // 模块代码
});

我想问一下 这里写['jquery']的作用是什么? 跟不写['jquery']有什么区别?
CMD是不是只支持但不提倡这种写法?

commented

其实对于CMD我也不熟。['jquery'] 这个似乎是依赖的js文件。

在 2014-06-25 10:40:19,"zhishaofei" notifications@github.com 写道:

define('hello',['jquery'],function(require,exports,module){var$=require("jquery");// 模块代码});

我想问一下 这里写['jquery']的作用是什么? 跟不写['jquery']有什么区别?
CMD是不是只支持但不提倡这种写法?


Reply to this email directly or view it on GitHub.

@jyk0011 是依赖的意思,但是我感觉不写也行 编译的时候自动会写上啊

commented

50分钟都没有入门,。。。

@Ihuali 我用了几个月才走到门口

通过exports对象增加成员向外提供接口 和
使用 return 直接向外提供接口 这两种方式有什么区别意义,请指教

exports 是 CommonJS 规范。而 return 是 AMD 和 CMD 规范的浏览器式的输出方式,比如在 node 上就不好用了。

@xuexb @jyk0011

在 2014-06-25 10:40:19,"zhishaofei" notifications@github.com 写道:
define('hello',['jquery'],function(require,exports,module){var$=require("jquery");// 模块代码});
我想问一下 这里写['jquery']的作用是什么? 跟不写['jquery']有什么区别?

我也有这样的疑惑,既然在模块内引用依赖文件需要使用require语法,那么这里的deps参数有什么作用的。
我自己测试,即使去掉了第二个参数(依赖数组),对后面使用require引入依赖也不会有任何影响。

比如当模块的接口是某个类的实例时,需要通过 module.exports 来实现:暴露一个类的实例不是这样:
exports.Slide = new Slide();对外接口是slide,怎么用module.exports暴露呢。。求解释

请教问题
index.html

<script src="/assets/js/sea/sea-debug.js"></script>
seajs.config({
    base: "/assets/js/",
    alias: {
        "underscore": "underscore-min.js",
        "jquery": "jquery.js"
    }
});

seajs.use('sea-modules/article/index');

index.js

define(function(require, exports, module) {

var aa = require('jquery');

alert(aa);  //null
 alert(jQuery);   // 可以打印出 对象

});

为什么上面 打印aa 为 null啊

jquery模块中定义时用的ID不对,可以在控制台看到报错的,请求不到jquery

在 2014年10月26日,4:34,greatming notifications@github.com 写道:

请教问题
index.html

index.js

define(function(require, exports, module) {

var aa = require('jquery');

alert(aa); //null
alert(jQuery); // 可以打印出 对象
});

为什么上面 打印aa 为 null啊


Reply to this email directly or view it on GitHub.

请求到了jquery.否则打印 alert(jquery)也不会出现内容。 发自网易邮箱手机版 在2014年10月26日 10:10,江枫写道: jquery模块中定义时用的ID不对,可以在控制台看到报错的,请求不到jquery > 在 2014年10月26日,4:34,greatming notifications@github.com 写道: > > 请教问题 > index.html > > index.js > > define(function(require, exports, module) { > > var aa = require('jquery'); > > alert(aa); //null > alert(jQuery); // 可以打印出 对象 > }); > > 为什么上面 打印aa 为 null啊 > > — > Reply to this email directly or view it on GitHub. — Reply to this email directly or view it on GitHub.

真的,这个是个烂大街的问题哦。可以在issue里面搜索下,一大丢

jquery模块是如何定义的

在 2014年10月26日,4:34,greatming notifications@github.com 写道:

请教问题
index.html

index.js

define(function(require, exports, module) {

var aa = require('jquery');

alert(aa); //null
alert(jQuery); // 可以打印出 对象
});

为什么上面 打印aa 为 null啊


Reply to this email directly or view it on GitHub.

seajs.config({
base: "/assets/js/",
alias: {
"underscore": "underscore-min.js",
"jquery": "jquery.js"
}
});

seajs.use('sea-modules/article/index');

引seajs的html页面有引jquery吗

在 2014年10月26日,10:38,greatming notifications@github.com 写道:

seajs.config({
base: "/assets/js/",
alias: {
"underscore": "underscore-min.js",
"jquery": "jquery.js"
}
});

seajs.use('sea-modules/article/index');


Reply to this email directly or view it on GitHub.

没有,只有引用了 < script src="/assets/js/sea/sea-debug.js">< /script>

并且 在 页面中
seajs.use("jquery",function($){
alert($); //null
alert(jQuery); //function
})

效果也是一样的

是不是要对 jquery.js 源码文件进行 cmd模块改造啊

require和require.async不可以同时存在吗?我两个同时使用时,如果require在前,require.async无法加载文件,如果require.async在前,则可以加载文件,但无法执行回调,这是什么情况,求解释!

define 中的id参数是不是只有在页面上写的时候有用?第二个依赖参数我加载后,没执行里面的alert方法,看到js文件已经加载了奇怪的很

不能exports={};的问题,只要sea.js里面加个判断就可以
if(exports!=module.exports){
module.exports=exports;
}

何必让使用者这么纠结

@yishou 这是 CommonJS Modules/1.0 的规范,不是 Sea.js 定的。可以看看 Node.js 的说明。https://nodejs.org/docs/latest/api/modules.html#modules_exports_alias

说句废话,学些了

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

commented

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

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

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

我用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'
}
}
请问这是什么原因呢?

commented

看这一段,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.

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

commented

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

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

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

commented

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

求内推XD