前言
如下圖:
AMD與CMD的主要區別:
- 1. 對於依賴的模塊,AMD 是提前執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。CMD 推崇 as lazy as possible.
- 2. CMD 推崇依賴就近,AMD 推崇依賴前置。看代碼:
// CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() var b = require('./b') b.doSomething() }) // AMD 默認推薦的是 define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好 a.doSomething() b.doSomething() })
AMD與CMD的其它區別可參考地址:https://www.zhihu.com/question/20351507
AMD
AMD規范可參考地址:https://github.com/amdjs/amdjs-api/wiki/AMD
AMD 是 RequireJS 在推廣過程中對模塊定義的規范化產出。
根據AMD規范,我們可以使用define定義模塊,使用require調用模塊,語法:
define(id?, dependencies?, factory);
- id: 定義中模塊的名字;可選;如果沒有提供該參數,模塊的名字應該默認為模塊加載器請求的指定腳本的名字;
- dependencies:依賴的模塊;
- factory:工廠方法,返回定義模塊的輸出值。
總結一段話:聲明模塊的時候指定所有的依賴dependencies,並且還要當做形參傳到factory中,對於依賴的模塊提前執行,依賴前置
例子1:
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); //Or: return require("beta").verb(); } });
例子2:
define(["alpha"], function (alpha) { return { verb: function(){ return alpha.verb() + 2; } }; });
例子3:
define({ add: function(x, y){ return x + y; } });
例子4:
define(function (require, exports, module) { var a = require('a'), b = require('b'); exports.action = function () {}; });
使用require函數加載模塊:
require([dependencies],function(){});
- 第一個參數是一個數組,表示所依賴的模塊
- 第二個參數是一個回調函數,當前面指定的模塊都加載成功后,它將被調用.加載的模塊會以參數形式傳入該函數,從而在回調函數內部就可以使用這些模塊
require()函數在加載依賴的函數的時候是異步加載的,這樣瀏覽器不會失去響應,它指定的回調函數,只有前面的模塊都加載成功后,才會運行,解決了依賴性的問題
require(['alpha'],function(alpha){ alpha.verb (); })
CMD
CMD規范參考地址:https://github.com/seajs/seajs/issues/242 CMD 是 SeaJS 在推廣過程中對模塊定義的規范化產出。
define Function
define
是一個全局函數,用來定義模塊。
define define(factory)
define
接受 factory
參數,factory
可以是一個函數,也可以是一個對象或字符串。
factory
為對象、字符串時,表示模塊的接口就是該對象、字符串。比如可以如下定義一個 JSON 數據模塊:
define({ "foo": "bar" });
也可以通過字符串定義模板模塊:
define('I am a template. My name is {{name}}.');
factory
為函數時,表示是模塊的構造方法。執行該構造方法,可以得到模塊向外提供的接口。factory
方法在執行時,默認會傳入三個參數:require
、exports
和 module
:
define(function(require, exports, module) { // 模塊代碼 });
define define(id?, deps?, factory)
define
也可以接受兩個以上參數。字符串 id
表示模塊標識,數組 deps
是模塊依賴。比如:
define('hello', ['jquery'], function(require, exports, module) { // 模塊代碼 });
id
和 deps
參數可以省略。省略時,可以通過構建工具自動生成。
注意:帶 id
和 deps
參數的 define
用法不屬於 CMD 規范,而屬於 Modules/Transport 規范。
define.cmd Object
一個空對象,可用來判定當前頁面是否有 CMD 模塊加載器:
if (typeof define === "function" && define.cmd) { // 有 Sea.js 等 CMD 模塊加載器存在 }
require Function
require
是 factory
函數的第一個參數。
require require(id)
require
是一個方法,接受 模塊標識 作為唯一參數,用來獲取其他模塊提供的接口。
define(function(require, exports) { // 獲取模塊 a 的接口 var a = require('./a'); // 調用模塊 a 的方法 a.doSomething(); });
require.async require.async(id, callback?)
require.async
方法用來在模塊內部異步加載模塊,並在加載完成后執行指定回調。callback
參數可選。
define(function(require, exports, module) { // 異步加載一個模塊,在加載完成時,執行回調 require.async('./b', function(b) { b.doSomething(); }); // 異步加載多個模塊,在加載完成時,執行回調 require.async(['./c', './d'], function(c, d) { c.doSomething(); d.doSomething(); }); });
注意:require
是同步往下執行,require.async
則是異步回調執行。require.async
一般用來加載可延遲異步加載的模塊。
exports Object
exports
是一個對象,用來向外提供模塊接口。
define(function(require, exports) { // 對外提供 foo 屬性 exports.foo = 'bar'; // 對外提供 doSomething 方法 exports.doSomething = function() {}; });
除了給 exports
對象增加成員,還可以使用 return
直接向外提供接口。
define(function(require) { // 通過 return 直接提供接口 return { foo: 'bar', doSomething: function() {} }; });
module Object
module
是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法。
module.id String
模塊的唯一標識。
define('id', [], function(require, exports, module) { // 模塊代碼 });
上面代碼中,define
的第一個參數就是模塊標識。
module.exports Object
當前模塊對外提供的接口。
傳給 factory
構造方法的 exports
參數是 module.exports
對象的一個引用。只通過 exports
參數來提供接口,有時無法滿足開發者的所有需求。 比如當模塊的接口是某個類的實例時,需要通過 module.exports
來實現:
define(function(require, exports, module) { // exports 是 module.exports 的一個引用 console.log(module.exports === exports); // true // 重新給 module.exports 賦值 module.exports = new SomeClass(); // exports 不再等於 module.exports console.log(module.exports === exports); // false });
CommonJS
CommonJS是服務器端模塊的規范,Node.js采用了這個規范.Node.JS首先采用了js模塊化的概念.
CommonJS定義的模塊分為:模塊引用(require)/ 模塊定義(exports)/模塊標識(module)
所有代碼都運行在模塊作用域,不會污染全局作用域。
模塊可以多次加載,但是只會在第一次加載時運行一次,然后運行結果就被緩存了,以后再加載,就直接讀取緩存結果。要想讓模塊再次運行,必須清除緩存。
模塊加載的順序,按照其在代碼中出現的順序。
//common.js module.exports = function(a, b) { return a-b } let minus = require('./common.js') //文件相對路徑 console.log(minus(5,4)) // 結果: 1
詳細可以參考《阮一峰:CommonJS規范》
ES6
導出
ES6兩種導出方式:
- 命名導出
- 默認導出
命名導出
// 寫法1 export const name = 'calculator'; export const add = function (a,b) { return a + b; } // 寫法2 const name = 'calculator'; const add = function (a,b) { return a + b; } export {name, add};
在使用命名導出時,可以通過as關鍵字對變量重命名。如:
const name = 'calculator'; const add = function (a,b) { return a + b; } export {name, add as getSum}; // 在導入時即為name 和 getSum
默認導出
export default { name: 'calculator';, add: function (a,b) { return a + b; } };
導入
針對命名導出的模塊,導入方式如下:
// calculator.js const name = 'calculator'; const add = function (a,b) { return a + b; } export {name, add}; // 一般導入方式 import {name, add} from './calculator.js' add(2,3); // 通過as關鍵字對導入的變量重命名 import {name, add as calculateSum} from './calculator.js' calculateSum(2,3); // 使用 import * as <myModule>可以把所有導入的變量作為屬性值添加到<myModule>對象中,從而減少了對當前作用域的影響 import * as calculateObj from './calculator.js' calculateObj.add(2,3);
對於默認導出來說,import后面直接跟變量名,並且這個名字可以自由指定,它指代了calculator.js中默認導出的值。從原理上可以這樣理解:
import {default as myCalculator} from './calculator.js'
還有兩種導入方式混合起來使用的例子,如下:
// index.js import React, {Component} from 'react';
注:這里的React必須寫在大括號前面,而不能順序顛倒,否則會提示語法錯誤。
復合寫法
在工程中,有時候需要把一個模塊導入后立即導出,比如專門用來集合所有頁面或組件的入口文件。此時可以采用復合形式的寫法:
export {name, add } from './calculator.js'
復合寫法只支持通過命名導出的方式暴露出來的變量,默認導出則沒有對應的復合形式,只能將導入和導出拆分開。
import calculator from './calculator.js' export default calculator;
UMD
UMD並不能說是一種模塊標准,不如說它是一組模塊形式的集合更准確。UMD的全稱是Universal Module Definition,也就是通用模塊標准。它的目標是使一個模塊能運行在各種環境下,不論是CommonJS、AMD,還是非模塊化的環境(當時ES6 Module還未被提出)
(function(root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(['jquery'], factory); } else if (typeof exports === 'object') { // Node, CommonJS之類的 module.exports = factory(require('jquery')); } else { // 瀏覽器全局變量(root 即 window) root.returnExports = factory(root.jQuery); } }(this, function($) { // 方法 function myFunc() {}; // 暴露公共方法 return myFunc; }));
詳細可參考:《可能是最詳細的UMD模塊入門指南》、《AMD , CMD, CommonJS,ES Module,UMD》