ความสัมพันธ์ระหว่าง 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.