ความสัมพันธ์ระหว่าง CommonJS, AMD และ RequireJS?


840

ฉันยังคงสับสนเกี่ยวกับCommonJS, AMDและRequireJSมากแม้หลังจากอ่านมาก

ฉันรู้ว่าCommonJS (เดิมชื่อServerJS ) เป็นกลุ่มสำหรับการกำหนดข้อกำหนดJavaScript (เช่นโมดูล) เมื่อมีการใช้ภาษานอกเบราว์เซอร์ ข้อกำหนดคุณสมบัติโมดูลCommonJSมีการนำไปใช้บางอย่างเช่นNode.jsหรือRingoJSใช่ไหม

ความสัมพันธ์ระหว่างCommonJS , Asynchronous Module Definition (AMD) และRequireJSคืออะไร?

RequireJSคือการนำไปใช้ของนิยามโมดูลCommonJSหรือไม่? ถ้าใช่เอเอ็มดีคืออะไร


31
การอ่านrequirejs.org/docs/whyamd.htmlจะอธิบายให้ชัดเจนมากเนื่องจากมันกล่าวถึงพวกเขาทั้งหมด (โพสต์มันเป็นความคิดเห็นที่ฉันไม่คิดว่านี่เป็นคำตอบเต็ม)
mmutilva

5
ฉันขอหรือเพิ่มได้ไหม; คำสั่งการนำเข้า ES2015 มีความสอดคล้องกับสิ่งเหล่านี้ทั้งหมดอย่างไร เช่นนำเข้า Ember จาก 'ember';
testndtv

นอกจากนี้ยังมี systemjs ที่โหลดรูปแบบโมดูล JS ที่สนับสนุนเช่น (CommonJS, UMD, AMD, ES6)
Andy

คำตอบ:


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 เริ่มต้นในรูปแบบของรูปแบบ CommonJS Transport และพัฒนาเป็น API คำจำกัดความของโมดูล ดังนั้นความคล้ายคลึงกันระหว่างคนทั้งสอง คุณลักษณะใหม่ใน AMD คือdefine()ฟังก์ชันที่ช่วยให้โมดูลสามารถประกาศการพึ่งพาก่อนที่จะโหลดได้ ตัวอย่างเช่นนิยามอาจเป็น:

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

ดังนั้น CommonJS และ AMD จึงเป็นAPI นิยามโมดูลJavaScriptที่มีการใช้งานที่แตกต่างกัน แต่ทั้งคู่มาจากต้นกำเนิดเดียวกัน

  • AMDเหมาะสำหรับเบราว์เซอร์มากกว่าเนื่องจากรองรับการโหลดแบบอะซิงโครนัสของการพึ่งพาโมดูล
  • RequireJSเป็นการใช้งานของAMDในขณะเดียวกันก็พยายามรักษาเจตนารมณ์ของCommonJS (ส่วนใหญ่อยู่ในโมดูลตัวระบุ)

เพื่อสร้างความสับสนให้กับคุณยิ่งกว่านี้ RequireJS ในขณะที่กำลังติดตั้ง AMD ก็นำเสนอ wrapper 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ที่เชื่อมช่องว่างของ 2 รูปแบบ - เขียนทั้ง (หรือทั้งสองอย่าง) ปรับใช้กับ <script> สองแบบหรือแบบง่าย
Angelos Pikoulas

51
FYI Browserifyจะให้คุณใช้ CommonJS ในเบราว์เซอร์
Eruant

9
@Eruant แต่ก็ยังไม่ได้เป็นแบบอะซิงโครนัสว่าเป็น AMD
Inanc Gumus

8
เหตุผลที่ว่าทำไม CommonJS ไม่เหมาะสมในเบราว์เซอร์ที่กล่าวถึงในเอกสาร RequireJS - "การ CommonJS ต้องการ () เป็นสายซิงโครก็คาดว่าจะกลับโมดูลทันทีนี้ไม่ได้ทำงานได้ดีในเบราว์เซอร์." ข้อมูลเพิ่มเติมที่นี่
msenni

4
@aaaaaa คุณอาจต้องการเปิดใช้งานคุณสมบัติบางอย่างขึ้นอยู่กับคำขอของผู้ใช้ ดังนั้นลักษณะ async ของ AMD อาจมีประโยชน์
Inanc Gumus

199

CommonJSเป็นมากกว่านั้น - เป็นโครงการเพื่อกำหนด API ทั่วไปและระบบนิเวศสำหรับ JavaScript ส่วนหนึ่งของ CommonJS คือข้อมูลจำเพาะของโมดูล Node.js และ RingoJS เป็นรันไทม์ JavaScript ฝั่งเซิร์ฟเวอร์และใช่ทั้งคู่ใช้งานโมดูลตามข้อมูลจำเพาะของโมดูล CommonJS

AMD (คำจำกัดความของโมดูลอะซิงโครนัส) เป็นอีกหนึ่งข้อกำหนดสำหรับโมดูล RequireJSน่าจะเป็นที่นิยมใช้มากที่สุดของ AMD ความแตกต่างที่สำคัญอย่างหนึ่งจาก CommonJS คือ AMD ระบุว่าโมดูลนั้นโหลดแบบอะซิงโครนัสซึ่งหมายความว่าโมดูลนั้นจะถูกโหลดแบบขนานซึ่งต่างจากการบล็อกการดำเนินการโดยรอให้โหลดเสร็จ

โดยทั่วไปแล้วเอเอ็มดีจะใช้ในการพัฒนาจาวาสคริปต์ในฝั่งไคลเอ็นต์ (ในเบราว์เซอร์) และโมดูล CommonJS นั้นมักจะใช้ฝั่งเซิร์ฟเวอร์ อย่างไรก็ตามคุณสามารถใช้ข้อมูลจำเพาะของโมดูลในสภาพแวดล้อมใด ๆ - ตัวอย่างเช่น RequireJS เสนอเส้นทางสำหรับการทำงานใน Node.jsและbrowserifyเป็นการใช้งานโมดูล CommonJS ที่สามารถทำงานในเบราว์เซอร์


20
ทำไมหน้าแรกของ CommonJS น่ากลัวมาก ... ฉันแค่พยายามดูสเป็คอย่างเป็นทางการ มีข้อผิดพลาดทางไวยากรณ์เอกสารที่ไม่สมบูรณ์และหน้า wiki ไม่ได้รับการแก้ไข
taco

7
นั่นไม่ใช่ความหมายในการโหลดโมดูลแบบอะซิงโครนัส คุณอาจกำลังพูดถึงการโหลดแบบไดนามิก / สันหลังยาว ด้วย async คุณแนะนำให้โหลดไฟล์จากนั้นอีกไม่นานมันจะโทรกลับเมื่อโหลดเสร็จแล้ว ด้วยการซิงค์คุณแนะนำให้โหลดไฟล์แล้วบล็อกเธรดทั้งหมดจนกว่าไฟล์นั้นจะโหลดเสร็จ ไม่มีรหัสเพิ่มเติมจนกว่าจะโหลดไฟล์ อดีตสามารถให้ประสิทธิภาพที่ดีขึ้นในราคาที่ไม่สามารถคาดการณ์ได้ในขณะที่หลังสามารถให้ผลลัพธ์เดียวกันทุกครั้งและสามารถคาดการณ์ได้มากขึ้น อย่าสังเกตว่านิสัยใจคอเหล่านี้สามารถลดลงได้โดยใช้การปรับให้เหมาะสมต่าง ๆ
เพอร์รี

ขอบคุณสำหรับคำตอบ. ตอนนี้โมดูลนั้นเป็นทางการใน JS กับ ES2015 นี่หมายความว่าโมดูลเหล่านั้นมีค่ามากกว่า AMD หรือ JS ทั่วไปหรือไม่?
Akhoy

มันไม่ได้หมายความว่าพวกเขาเป็นที่ต้องการ ทุกอย่างขึ้นอยู่กับความต้องการของนักพัฒนา ฉันไม่คิดว่าการไม่มีตัวเลือกและการไปใช้กับโมดูล ES6 เป็นความคิดที่ดีโดยเฉพาะ ด้วยการใช้ UMD ที่ดีคุณสามารถต่อสู้กับปัญหานั้นได้ การโหลดบันเดิล CommonJS ที่ซิงค์กับ AMD เป็นแนวคิดที่ดี (ดีที่สุด) โดยทั่วไป (สำหรับการปรับปรุงเพื่อประโยชน์ด้านประสิทธิภาพ) หากคุณรู้สึกว่าคุณควรควบคุมได้ชัดเจนขึ้น และคุณควร
Maciej Sitko

187

คำตอบสั้น ๆ คือ:

CommonJSและ AMDเป็นข้อกำหนด (หรือรูปแบบ) เกี่ยวกับวิธีที่โมดูลและการขึ้นต่อกันควรถูกประกาศในแอปพลิเคชันจาวาสคริปต์

RequireJSเป็นไลบรารีโหลดเดอร์สคริปต์ที่เป็นไปตามมาตรฐานของ AMD curljเป็นอีกตัวอย่างหนึ่ง

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 และเป็นเพียงส่วนหนึ่งเท่านั้นที่เกี่ยวข้องกับสิ่งนั้น เอเอ็มดีเริ่มต้นเป็นข้อกำหนดร่างสำหรับรูปแบบโมดูลในรายการ CommonJS แต่ฉันทามติเต็มไม่ถึงและพัฒนาต่อไปของรูปแบบย้ายไปกลุ่ม amdjs ข้อโต้แย้งเกี่ยวกับรูปแบบที่ระบุว่า CommonJS พยายามครอบคลุมชุดของปัญหาที่กว้างขึ้นและเหมาะสำหรับการพัฒนาด้านเซิร์ฟเวอร์เนื่องจากลักษณะการซิงโครนัสและ AMD เหมาะสำหรับการพัฒนาฝั่งไคลเอ็นต์ (เบราว์เซอร์) เนื่องจากลักษณะอะซิงโครนัสและ ความจริงที่ว่ามันมีรากฐานในการดำเนินการประกาศโมดูลของ Dojo

แหล่งที่มา:


1
การเห็นโค้ดมากกว่าคำอธิบายช่วยได้! :) AMD compliantต้องใช้ JS ใช่ไหม?
Asim KT

ฉันพลาดอะไรบางอย่างหรือมีบางอย่างผิดพลาด? คุณกำหนด "package / lib" แต่ต้องใช้ "package / myModule"
RullDawg

ฉันมักจะชอบอ่านประวัติเกี่ยวกับสาเหตุที่มีบางอย่างเป็นไปได้เสมอ! ขอบคุณที่ให้พื้นหลังนั้น!
Andru

@RullDawg ไม่“ package / lib” ไม่ได้ถูกกำหนดไว้ที่นี่มันเป็นการพึ่งพาบุคคลที่สามที่ใช้ที่นี่
Robert Siemer

28

quoting

AMD :

  • วิธีแรกในเบราว์เซอร์
  • การเลือกพฤติกรรมแบบอะซิงโครนัสและความเข้ากันได้แบบย้อนหลังที่ง่ายขึ้น
  • ไม่มีแนวคิดของ File I / O
  • มันสนับสนุนวัตถุฟังก์ชันคอนสตรัคเตอร์สตริง JSON และโมดูลประเภทอื่น ๆ อีกมากมาย

CommonJS :

  • วิธีแรกสำหรับเซิร์ฟเวอร์
  • สมมติว่าพฤติกรรมแบบซิงโครนัส
  • ครอบคลุมชุดของข้อกังวลที่กว้างขึ้นเช่น I / O, ระบบไฟล์, สัญญาและอื่น ๆ
  • สนับสนุนโมดูลที่ไม่ได้เปิดใช้งานมันสามารถรู้สึกใกล้เคียงกับข้อกำหนดES.next/Harmonyเพียงเล็กน้อยช่วยให้คุณใช้ wrapper define () ที่AMDบังคับใช้
  • สนับสนุนวัตถุเป็นโมดูลเท่านั้น

17

มันเป็นเรื่องปกติมากทีเดียวที่จะจัดระเบียบ JavaScript modular โปรแกรมลงในไฟล์หลายและการเรียกร้องจากchild-modulesmain js module

สิ่งนี้คือ JavaScript ไม่ได้ให้สิ่งนี้ แม้กระทั่งทุกวันนี้ใน Chrome และ FF เวอร์ชันล่าสุดของเบราว์เซอร์

แต่มีคำหลักใน JavaScript ที่เรียกโมดูล JavaScript อื่นหรือไม่

คำถามนี้อาจจะมีการยุบรวมของโลกสำหรับหลาย ๆ คนเพราะคำตอบคือไม่มี


ใน ES5 (ปล่อยตัวในปี 2009) มี JavaScript คำหลักที่ไม่เหมือนใครนำเข้า , รวมหรือต้อง

ES6 บันทึกวัน (เปิดตัวในปี 2015) เสนอคำหลักนำเข้า ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/import ) แต่ไม่มีเบราว์เซอร์ที่ใช้สิ่งนี้

หากคุณใช้ Babel 6.18.0 และ transpile พร้อมตัวเลือก 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 จะจัดการทุกอย่างตั้งแต่การอ่านไฟล์ระดับระบบไปจนถึงการห่อฟังก์ชั่นลงในโมดูล

เพราะในฟังก์ชั่นจาวาสคริปต์เป็นเพียงเครื่องมือห่อหุ้มเพื่อเป็นตัวแทนโมดูล

ฉันสับสนมากเกี่ยวกับ CommonJS และ AMD ใช่ไหม

ทั้ง CommonJS และ AMD เป็นเพียงสองเทคนิคที่แตกต่างกันวิธีเอาชนะ JavaScript "ข้อบกพร่อง" เพื่อโหลดโมดูลสมาร์ท


3
ควรอัปเดตคำตอบของคุณเพราะขณะนี้เบราว์เซอร์ที่ทันสมัยรองรับทั้งหมด import
vsync

@vsync ใช่รู้สึกอิสระที่จะแก้ไขคำตอบของฉันเนื่องจากฉันไม่ได้ติดตามกลุ่มนี้มาระยะหนึ่งแล้ว
prosti
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.