前言
主要學習一下四種模塊加載規范:
- AMD
- CMD
- CommonJS
- ES6 模塊
歷史
require.js
requirejs 為全局添加了 define 函數,你只要按照這種約定的方式書寫這個模塊即可。
define(function () { //Do setup work here return { color: "black", size: "unisize" } });
//my/shirt.js now has some dependencies, a cart and inventory //module in the same directory as shirt.js define(["./cart", "./inventory"], function(cart, inventory) { //return an object to define the "my/shirt" module. return { color: "blue", size: "large", addToCart: function() { inventory.decrement(this); cart.add(this); } } } );
以上示例代碼來源於require.js官網
demo代碼詳見 https://github.com/BillyQin/jsModule/tree/master/requireJs
AMD
require.js 為全局添加了define 函數,按照這種約定方式寫即可。
這個約定方式就是AMD(The Asyncchronous Module Definition)
所以AMD規范就是定義了怎么寫define函數。只要按照這個規范來寫模塊和依賴,require.js就能正確解析。
sea.js
demo代碼詳見 https://github.com/BillyQin/jsModule/tree/master/seaJs
CMD
同樣的道理,CMD就是Sea.js對模塊定義對規范化產出。
所以CMD的內容就是描述該如何定義模塊,如何引入模塊,如何導出模塊。只要按照這個規范來寫模塊和依賴,sea.js就能正確解析。
AMD 和 CMD
- AMD 推崇依賴前置,
-
CMD推崇依賴就近
- 對於依賴的模塊,AMD 是提前執行,CMD 是延遲執行。
- AMD 是將需要使用的模塊先加載完再執行代碼
- CMD 是在 require 的時候才去加載模塊文件,加載完再接着執行。
CommonJS
AMD 和 CMD 都是用於瀏覽器的模塊規范,而在服務端(node),則采用CommonJS。
CommonJS和sea.js一樣,require的時候才去加載模塊文件,加載完再接着執行。
demo代碼詳見 https://github.com/BillyQin/jsModule/tree/master/commonJs
為什么瀏覽器中不支持 CommonJS 語法呢?
這是因為瀏覽器環境中並沒有 module、 exports、 require 等環境變量。
ES6
es6定義了新的模塊加載方案。
// 導出 const addr = 'China' const year = 2018 export { addr, year }
// 導入 import { addr, year } from './index.js'
和require.js(AMD)一致,將需要使用的模塊加載完再執行代碼。
ES6 和 CommonJS的差異
-
CommonJS模塊輸出值的拷貝, ES6輸出值的引用。 CommonJS模塊輸出值的拷貝, 也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。
-
CommonJS是運行時加載,ES6是編譯時輸出接口。 CommonJS加載的是一個對象,就是module.exports屬性。該對象只有在腳本運行完成后才會生成。而es6模塊不是對象,對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。
Babel
在瀏覽器不支持es6的時候,如果要使用es6的語法,一般都會在項目里加入babel。
// es6 let firstName = 'Michael'; const lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year};
轉換后
Object.defineProperty(exports, "__esModule", { value: true }); var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; exports.firstName = firstName; exports.lastName = lastName; exports.year = year;
webpack
Babel 只是把 ES6 模塊語法轉為 CommonJS 模塊語法,而瀏覽器不支持CommonJs。這時候webpack出動。
瀏覽器不支持CommonJs的本質是因為瀏覽器環境中並沒有 module、 exports、 require 等環境變量。 webpack 打包后的文件之所以在瀏覽器中能運行,就是靠模擬了這些變量的行為。
webpack怎么模擬呢?
// commonJs let multiply = require('./multiply') console.log('加載 square 模塊') let square = function (num) { return multiply.multiply(num, num) } module.exports = { square: square }
模擬后:
// 包裹一層,注入這些變量 function(module, exports, require) { console.log('加載了 square 模塊'); var multiply = require("./multiply"); module.exports = { square: function(num) { return multiply.multiply(num, num); } }; }
整個CommonJs項目改寫后
// 自執行函數 (function(modules){ // 存儲已加載的模塊 var installModules = {} // 關鍵的require方法 function require(moduleName) { if (installModules.moduleName) { return installModules.moduleName.exports } var module = installModules[moduleName] = { exports: {} } modules[moduleName](module, module.exports, require); return module.exports; } return require('main') })({ 'main': function(module, exports, require) { var addModule = require("./add"); console.log(addModule.add(1, 1)) var squareModule = require("./square"); console.log(squareModule.square(3)); }, './add': function(module, exports, require) { console.log('加載 add 模塊') var add = function (x, y) { return x + y } module.exports = { add: add } }, './multiply': function(module, exports, require) { console.log('加載 multiply 模塊') var multiply = function (x, y) { return x * y } module.exports = { multiply: multiply } }, './square': function(module, exports, require) { console.log('加載 square 模塊') var multiply = require('./multiply') var square = function (num) { return multiply.multiply(num, num) } module.exports = { square: square } } })