Relación entre CommonJS, AMD y RequireJS?

840

Todavía estoy muy confundido acerca de CommonJS, AMD y RequireJS , incluso después de leer mucho.

Sé que CommonJS (anteriormente ServerJS ) es un grupo para definir algunas especificaciones de JavaScript (es decir, módulos) cuando el idioma se usa fuera del navegador. La especificación de módulos CommonJS tiene alguna implementación como Node.js o RingoJS , ¿verdad?

¿Cuál es la relación entre CommonJS , Asynchronous Module Definition (AMD) y RequireJS ?

¿ RequireJS es una implementación de la definición del módulo CommonJS ? Si es así, ¿qué es AMD entonces?

gremo
fuente
31
Leer requirejs.org/docs/whyamd.html aclararía mucho, ya que los menciona a todos. (publíquelo como un comentario ya que no considero que sea una respuesta completa).
mmutilva
55
¿Puedo preguntar o agregar más? ¿Cómo o dónde encajan las declaraciones de importación ES2015 en todo esto? por ejemplo, importar Ember de 'ember';
testndtv
También hay un systemjs que carga cualquiera de los formatos de módulo JS compatibles (CommonJS, UMD, AMD, ES6).
Andy

Respuestas:

770

RequireJS implementa la API de AMD (fuente) .

CommonJS es una forma de definir módulos con la ayuda de un exportsobjeto, que define el contenido del módulo. En pocas palabras, una implementación de CommonJS podría funcionar así:

// 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"; };

Básicamente, CommonJS especifica que debe tener una require()función para buscar dependencias, una exportsvariable para exportar el contenido del módulo y un identificador de módulo (que describe la ubicación del módulo en cuestión en relación con este módulo) que se utiliza para requerir las dependencias ( fuente ) CommonJS tiene varias implementaciones, incluyendo Node.js , que mencionó.

CommonJS no fue especialmente diseñado con los navegadores en mente, por lo que no encaja muy bien en el entorno del navegador ( realmente no tengo una fuente para esto, solo lo dice en todas partes, incluido el sitio RequireJS ) . Aparentemente, esto tiene algo que hacer con carga asincrónica, etc.

Por otro lado, RequireJS implementa AMD, que está diseñado para adaptarse al entorno del navegador ( fuente ). Aparentemente, AMD comenzó como un spin-off del formato de transporte CommonJS y evolucionó hacia su propia API de definición de módulo. De ahí las similitudes entre los dos. La nueva característica de AMD es la define()función que permite al módulo declarar sus dependencias antes de cargarlo. Por ejemplo, la definición podría ser:

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

Entonces, CommonJS y AMD son API de definición de módulo de JavaScript que tienen implementaciones diferentes, pero ambas provienen del mismo origen.

  • AMD es más adecuado para el navegador, ya que admite la carga asincrónica de dependencias de módulos.
  • RequireJS es una implementación de AMD , al mismo tiempo que trata de mantener el espíritu de CommonJS (principalmente en los identificadores del módulo).

Para confundirlo aún más, RequireJS, aunque es una implementación de AMD, ofrece un contenedor CommonJS para que los módulos CommonJS se puedan importar casi directamente para usar con RequireJS.

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

¡Espero que esto ayude a aclarar las cosas!

jakee
fuente
77
Consulte el proyecto uRequire.org que cierra las brechas de los 2 formatos: escriba en cualquiera (o en ambos), implemente en cualquiera de los dos o <script> simple
Angelos Pikoulas
51
FYI Browserify ahora le permitirá usar CommonJS en el navegador.
Eruant
99
@Eruant Pero, todavía no tiene esa naturaleza asincrónica como AMD.
Inanc Gumus
8
La razón por la cual CommonJS no cabe en el navegador como se menciona en los documentos RequireJS: "CommonJS require () es una llamada síncrona, se espera que devuelva el módulo de inmediato. Esto no funciona bien en el navegador" . Más información aquí .
msenni
44
@aaaaaa es posible que desee habilitar algunas funciones según la solicitud del usuario; entonces la naturaleza asíncrona de AMD puede ser útil.
Inanc Gumus
199

CommonJS es más que eso: es un proyecto para definir una API y un ecosistema comunes para JavaScript. Una parte de CommonJS es la especificación del módulo . Node.js y RingoJS son tiempos de ejecución de JavaScript del lado del servidor, y sí, ambos implementan módulos basados ​​en las especificaciones del Módulo CommonJS.

AMD (definición de módulo asíncrono) es otra especificación para módulos. RequireJS es probablemente la implementación más popular de AMD. Una diferencia importante de CommonJS es que AMD especifica que los módulos se cargan de forma asincrónica , lo que significa que los módulos se cargan en paralelo, en lugar de bloquear la ejecución al esperar que termine una carga.

AMD generalmente se usa más en el desarrollo de JavaScript del lado del cliente (en el navegador) debido a esto, y los módulos CommonJS generalmente se usan del lado del servidor. Sin embargo, puede usar cualquiera de las especificaciones del módulo en cualquier entorno; por ejemplo, RequireJS ofrece instrucciones para ejecutarse en Node.js y browserify es una implementación del Módulo CommonJS que puede ejecutarse en el navegador.

Nate
fuente
20
¿Por qué la página de inicio de CommonJS es tan horrible? Solo estoy tratando de ver las especificaciones oficiales. Tiene errores de sintaxis, documentación incompleta y la página wiki no se está resolviendo.
taco
77
Eso no es lo que significa cargar módulos de forma asincrónica. Tal vez estés hablando de carga dinámica / lenta. Con async, sugiere un archivo para cargar y luego, un tiempo después, volverá a llamar cuando haya terminado de cargarse. Con la sincronización, sugiere un archivo para cargar y luego el subproceso completo se bloquea hasta que el archivo haya terminado de cargarse; no se ejecuta más código hasta que se carga el archivo. El primero puede producir un mejor rendimiento a costa de la imprevisibilidad, mientras que el segundo puede producir los mismos resultados cada vez y, por lo tanto, es más predecible. Tenga en cuenta que estas peculiaridades se pueden mitigar mediante varias optimizaciones.
Perry
Gracias por la respuesta. Ahora que los módulos son oficiales en JS con ES2015, ¿significa esto que se prefieren más que AMD o JS común?
Akhoy
No significa que sean preferidos. Todo depende de las necesidades del desarrollador. No creo que dejar ninguna opción y optar por módulos ES6 sea una idea especialmente buena. Sin embargo, con un buen UMD, puedes combatir ese problema. Cargar paquetes de CommonJS sincronizados con AMD es una buena (mejor) idea en general (para mejorar el rendimiento). Si sientes que deberías tener más control, obviamente. Y deberías
Maciej Sitko
187

La respuesta corta sería:

CommonJS y AMD son especificaciones (o formatos) sobre cómo deben declararse los módulos y sus dependencias en las aplicaciones javascript.

RequireJS es una biblioteca de cargadores de scripts que cumple con AMD, curljs es otro ejemplo.

CommonJS compatible:

Tomado del libro de 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;

Cumple con 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
    }
});

En otro lugar, el módulo se puede usar con:

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

Algunos antecedentes:

En realidad, CommonJS es mucho más que una declaración de API y solo una parte de ella se ocupa de eso. AMD comenzó como un borrador de especificación para el formato del módulo en la lista CommonJS, pero no se llegó a un consenso total y el desarrollo posterior del formato se trasladó al grupo amdjs . Los argumentos en torno a qué formato es mejor indican que CommonJS intenta cubrir un conjunto más amplio de preocupaciones y que es más adecuado para el desarrollo del lado del servidor dada su naturaleza sincrónica, y que AMD es más adecuado para el desarrollo del lado del cliente (navegador) dada su naturaleza asincrónica y hecho de que tiene sus raíces en la implementación de la declaración del módulo de Dojo.

Fuentes:

mmutilva
fuente
1
¡Ver código en lugar de descripciones ayuda! :) en AMD compliantrealidad es RequireJS, ¿verdad?
Asim KT
¿Me estoy perdiendo algo o hay algo mal escrito? Usted define "package / lib" pero luego requiere "package / myModule".
RullDawg
¡Siempre me gusta leer un poco sobre la historia de por qué algo es así! Gracias por proporcionar ese fondo!
Andru
@RullDawg No, "package / lib" no se define aquí, es una dependencia de terceros utilizada aquí.
Robert Siemer
28

Citando

AMD :

  • Un enfoque de primer navegador
  • Optar por un comportamiento asincrónico y una compatibilidad hacia atrás simplificada
  • No tiene ningún concepto de E / S de archivo.
  • Admite objetos, funciones, constructores, cadenas, JSON y muchos otros tipos de módulos.

CommonJS :

  • Un primer enfoque de servidor
  • Asumiendo un comportamiento sincrónico
  • Cubre un conjunto más amplio de preocupaciones como E / S, sistema de archivos, promesas y más.
  • Admite módulos sin envolver, puede sentirse un poco más cerca de las especificaciones ES.next/Harmony , liberándolo del envoltorio define () que AMDaplica.
  • Solo admite objetos como módulos.
zangw
fuente
17

Es bastante normal organizar el programa JavaScript modular en varios archivos y llamar child-modulesdesde el main js module.

La cuestión es que JavaScript no proporciona esto. Ni siquiera hoy en las últimas versiones de navegador de Chrome y FF.

Pero, ¿hay alguna palabra clave en JavaScript para llamar a otro módulo de JavaScript?

Esta pregunta puede ser un colapso total del mundo para muchos porque la respuesta es no .


En ES5 (lanzado en 2009), JavaScript no tenía palabras clave como importar , incluir o requerir .

ES6 salva el día (lanzado en 2015) proponiendo la palabra clave de importación ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/import ), pero ningún navegador implementa esto.

Si usa Babel 6.18.0 y transpila solo con la opción ES2015

import myDefault from "my-module";

obtendrá requirede nuevo.

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

Esto se debe a que requireel módulo se cargará desde Node.js. Node.js se encargará de todo, desde la lectura de archivos a nivel del sistema hasta las funciones de ajuste en el módulo.

Porque en JavaScript las funciones son los únicos contenedores para representar los módulos.

Estoy muy confundido acerca de CommonJS y AMD?

Tanto CommonJS como AMD son solo dos técnicas diferentes para superar el "defecto" de JavaScript para cargar módulos de manera inteligente.

prosti
fuente
3
Debería actualizar su respuesta porque ahora todos los navegadores modernos son compatibles import
vsync
@vsync, sí, siéntase libre de editar mi respuesta, ya que no he seguido este segmento por algún tiempo.
prosti