我仍然对CommonJS, AMD和RequireJS很困惑,即使在阅读了很多之后。

我知道CommonJS(以前的ServerJS)是一个定义JavaScript语言在浏览器外使用时的一些规范(即模块)的组织。CommonJS模块规范有一些实现比如node。js或者RingoJS,对吧?

CommonJS,异步模块定义(AMD)和RequireJS之间的关系是什么? RequireJS是CommonJS模块定义的实现吗?如果是,AMD是什么?


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

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

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


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一开始是CommonJS传输格式的衍生品,后来发展成了自己的模块定义API。因此两者有相似之处。AMD的新特性是define()函数,它允许模块在加载之前声明它的依赖项。例如,定义可以是:

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

所以,CommonJS和AMD是JavaScript模块定义api,它们有不同的实现,但它们都来自同一个起源。

AMD更适合浏览器,因为它支持异步加载模块依赖项。 RequireJS是AMD的一个实现,同时试图保持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"; };
});

简短的回答是:

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();
});

背景知识:

Actually, CommonJS is much more than an API declaration and only a part of it deals with that. AMD started as a draft specification for the module format on the CommonJS list, but full consensus wasn't reached and further development of the format moved to the amdjs group. Arguments around which format is better state that CommonJS attempts to cover a broader set of concerns and that it's better suited for server side development given its synchronous nature, and that AMD is better suited for client side (browser) development given its asynchronous nature and the fact that it has its roots in Dojo's module declaration implementation.

来源:

为什么是AMD? Addy Osmani -学习JavaScript设计模式-现代模块化JavaScript设计模式


引用

AMD:

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

CommonJS:

一种服务器优先的方法 假设同步行为 涵盖更广泛的问题集,如I/O,文件系统,承诺等。 支持未包装模块,感觉更接近ES。next/Harmony规范,将你从AMD强制的define()包装器中解放出来。 只支持对象作为模块。


将JavaScript程序模块组织成几个文件并从主js模块调用子模块是很正常的。

问题是JavaScript没有提供这个。即使在最新版本的Chrome和FF浏览器中也没有。

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

对许多人来说,这个问题可能是世界的彻底崩溃,因为答案是否定的。


在ES5(2009年发布)中,JavaScript没有import、include或require这样的关键字。

ES6挽救了局面(2015年发布)提出了import关键字(https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/import),现在所有现代浏览器都支持它。

如果您使用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的“缺陷”来智能加载模块。


AMD

在JavaScript中引入将JavaScript项目扩展到多个文件 主要用于基于浏览器的应用程序和库 流行的实现是RequireJS, Dojo Toolkit

CommonJS:

它是处理大项目中大量功能、文件和模块的规范 ServerJS的初始名称是Mozilla在2009年1月引入的 2009年8月更名为CommonJS,以显示api更广泛的适用性 最初的实现是服务器,nodejs,基于桌面的库

例子

upper.js文件

exports.uppercase = str => str.toUpperCase()

main.js文件

const uppercaseModule = require('uppercase.js')
uppercaseModule.uppercase('test')

总结

AMD——最古老的模块系统之一,最初由require.js库实现。 CommonJS -为Node.js服务器创建的模块系统。 UMD -一个多模块系统,建议作为一个通用系统,兼容AMD和CommonJS。

资源:

https://exploringjs.com/es6/ch_modules.html#sec_modules-in-javascript 维基百科