CommonJS,AMD和RequireJS之间的关系?


840

即使阅读了很多,我仍然对CommonJS,AMDRequireJS感到非常困惑。

我知道CommonJS(以前称为ServerJS)是用于在浏览器之外使用该语言时定义一些JavaScript规范(即模块)的组。CommonJS模块规范具有一些实现,例如Node.jsRingoJS,对吗?

CommonJS异步模块定义(AMD)和RequireJS之间是什么关系?

RequireJS的的实现CommonJS的模块定义?如果是,那么AMD是什么?


31
阅读requirejs.org/docs/whyamd.html会很清楚,因为它提到了所有这些内容。(将其发布为评论,因为我不认为这是完整的答案)。
mmutilva 2013年

5
我可以提出更多要求吗?ES2015导入语句如何或在哪里适合所有这些;例如从“ ember”导入Ember;
testndtv

还有一个systemjs加载任何受支持的JS模块格式,例如(CommonJS,UMD,AMD,ES6)。
安迪

Answers:


770

RequireJS实现AMD API (源代码)

CommonJS是在exports对象的帮助下定义模块的方法,该对象定义了模块的内容。简而言之,CommonJS实现可能如下所示:

// someModule.js
exports.doSomething = function() { return "foo"; };

//otherModule.js
var someModule = require('someModule'); // in the vein of node    
exports.doSomethingElse = function() { return someModule.doSomething() + "bar"; };

基本上,CommonJS指定您需要具有一个require()用于获取依赖项的函数,一个exports用于导出模块内容的变量以及一个用于要求依赖项的模块标识符(描述了该模块相对于该模块的位置)()。CommonJS具有各种实现,包括您提到的Node.js。

CommonJS并不是专门为浏览器而设计的,因此它不太适合浏览器环境(我确实没有相关资源-它在包括RequireJS网站在内所有地方都这么说)显然,这有一些不足之处异步加载等

另一方面,RequireJS实现了AMD,该AMD旨在适应浏览器环境(source)。显然,AMD是从CommonJS Transport格式的衍生产品开始的,并演变为自己的模块定义API。因此,两者之间的相似之处。AMD中的新功能是define()允许模块在加载之前声明其依赖性的功能。例如,定义可以是:

define('module/id/string', ['module', 'dependency', 'array'], 
function(module, factory function) {
  return ModuleContents;  
});

因此,CommonJS和AMD是JavaScript模块定义API,它们具有不同的实现,但是它们来自相同的来源。

  • AMD更适合于浏览器,因为它支持异步加载模块依赖项。
  • RequireJSAMD的实现,同时尝试保持CommonJS的精神(主要在模块标识符中)。

更令人困惑的是,RequireJS在作为AMD实现时提供了CommonJS包装器,因此CommonJS模块几乎可以直接导入以与RequireJS一起使用。

define(function(require, exports, module) {
  var someModule = require('someModule'); // in the vein of node    
  exports.doSomethingElse = function() { return someModule.doSomething() + "bar"; };
});

我希望这有助于澄清问题!


7
uRequire.org项目,该项目弥合了两种格式之间的差距-以一种(或两种)书写,部署到两种或任何一种简单的<script>
Angelos Pikoulas 2013年

51
FYI Browserify现在将允许您在浏览器中使用CommonJS。
Eruant 2014年

9
@Eruant但是,它仍然没有AMD那样的异步特性。
伊南克·古姆斯

8
RequireJS文档中提到了CommonJS不适合浏览器的原因- “ CommonJS require()是一个同步调用,应该立即返回该模块。这在浏览器中不能很好地工作”。更多信息在这里
msenni'1

4
@aaaaaa,您可能需要根据用户请求启用某些功能;因此AMD的异步特性可能会派上用场。
伊南克·古姆斯

199

CommonJS不仅限于此-它是一个为JavaScript定义通用API和生态系统的项目。CommonJS的一部分是模块规范。Node.js和RingoJS是服务器端JavaScript运行时,是的,它们都基于CommonJS Module规范实现模块。

AMD(异步模块定义)是另一种模块规范。RequireJS可能是AMD最受欢迎的实现。与CommonJS的主要区别在于,AMD指定模块是异步加载的-这意味着模块是并行加载的,而不是通过等待加载完成来阻止执行。

因此,AMD通常更多地用于客户端(浏览器)JavaScript开发中,而CommonJS模块通常用于服务器端。但是,您可以在任何环境中使用任何一种模块规范-例如,RequireJS提供了在Node.js中运行的说明,browserify是可以在浏览器中运行的CommonJS Module实现。


20
为什么CommonJS主页如此可怕...我只是想查看正式规范。它存在语法错误,文档不完整,并且Wiki页面无法解析。

7
这不是异步加载模块的含义。您可能正在谈论动态/延迟加载。使用异步,您建议加载一个文件,然后过一会儿,它会在完成加载后回调。使用同步,您建议先加载一个文件,然后整个线程都阻塞,直到该文件完成加载为止。在文件加载之前,不会执行其他代码。前者可以以不可预测的代价获得更好的性能,而后者每次都能产生相同的结果,因此更可预测。请注意,可以使用各种优化来缓解这些怪癖。
perry

感谢您的回答。现在,随着ES2015在JS中正式使用模块,这是否意味着它们比AMD或通用JS更受青睐?
Akhoy '16

这并不意味着它们是首选。这完全取决于开发人员的需求。我认为没有选择并选择ES6模块并不是一个好主意。但是,使用良好的UMD,您可以解决此问题。总的来说,加载与AMD同步的CommonJS捆绑包是一个好主意(为了提高性能)。显然,如果您觉得自己应该有更多的控制权。那你应该
Maciej Sitko'5

187

简短的答案是:

CommonJS AMD是有关应如何在javascript应用程序中声明模块及其依赖关系的规范(或格式)。

RequireJS是一个符合AMD要求的脚本加载程序库,另一个示例是 curljs

符合CommonJS:

摘自Addy Osmani的书

// package/lib is a dependency we require
var lib = require( "package/lib" );

// behavior for our module
function foo(){
    lib.log( "hello world!" );
}

// export (expose) foo to other modules as foobar
exports.foobar = foo;

符合AMD标准:

// package/lib is a dependency we require
define(["package/lib"], function (lib) {

    // behavior for our module
    function foo() {
        lib.log( "hello world!" );
    }

    // export (expose) foo to other modules as foobar
    return {
        foobar: foo
    }
});

该模块可以在其他地方使用:

require(["package/myModule"], function(myModule) {
    myModule.foobar();
});

一些背景:

实际上,CommonJS不仅仅是一个API声明,而且其中只有一部分处理该声明。AMD最初是CommonJS列表中模块格式的规范草案,但尚未达成完全共识,因此该格式的进一步开发移交给了amdjs组。关于哪种格式更好的争论指出,CommonJS试图涵盖更广泛的关注点,并且鉴于其同步特性,它更适合于服务器端开发,而鉴于其异步特性和特性,AMD更适合于客户端(浏览器)开发。它起源于Dojo的模块声明实现这一事实。

资料来源:


1
看代码而不是描述会有所帮助!:) AMD compliant实际上是RequireJS,对吗?
Asim KT

我错过了什么吗?还是打错了什么?您定义“ package / lib”,但随后需要“ package / myModule”。
RullDawg '16

我总是喜欢阅读一些有关为何事物发展的历史!感谢您提供背景信息!
安德鲁

@RullDawg否,此处未定义“ package / lib”,此处使用的是第三方依赖关系。
罗伯·西默

28

报价单

AMD

  • 一种浏览器优先的方法
  • 选择异步行为并简化向后兼容性
  • 它没有文件I / O的任何概念。
  • 它支持对象,函数,构造函数,字符串,JSON和许多其他类型的模块。

CommonJS

  • 一种服务器优先的方法
  • 假设同步行为
  • 涵盖更广泛的关注点,例如I / O,文件系统,承诺等。
  • 支持展开的模块,可以感觉到更接近ES.next/Harmony规范,从而使您摆脱了AMD强制执行的define()包装器。
  • 仅支持将对象作为模块。

17

将JavaScript程序模块化组织为多个文件并从中调用是很正常child-modulesmain js module

问题是JavaScript不提供此功能。直到今天,最新版的Chrome和FF浏览器都没有。

但是,JavaScript中是否有关键字可以调用另一个JavaScript模块?

这个问题可能是世界上的许多完全崩溃,因为答案是没有


在ES5(于2009年发布)中,JavaScript没有诸如importincluderequire之类的关键字。

ES6节省了建议使用import关键字( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/import)的日期(于2015年发布),但是没有浏览器实现此目的。

如果您使用Babel 6.18.0并仅通过ES2015选项进行转换

import myDefault from "my-module";

你会require再次得到。

"use strict";
var _myModule = require("my-module");
var _myModule2 = _interopRequireDefault(_myModule);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

这是因为require意味着将从Node.js加载模块。Node.js将处理从系统级文件读取到将函数包装到模块中的所有内容。

因为在JavaScript中,函数是表示模块的唯一包装。

我对CommonJS和AMD感到困惑?

CommonJS和AMD都是仅两种不同的技术,可以克服JavaScript的“缺陷”以智能地加载模块。


3
应该更新您的答案,因为现在所有现代浏览器都支持 import
vsync

@vsync,是的,请随时编辑我的答案,因为我已经有一段时间没有关注此部分了。
prosti
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.