一.CommonJS
1.概念
????????CommonJS 規范概述了同步聲明依賴的模塊定義。這個規范主要用于在服務器端實現模塊化代碼組 織,但也可用于定義在瀏覽器中使用的模塊依賴。CommonJS 模塊語法不能在瀏覽器中直接運行;在瀏覽器端,模塊需要提前編譯打包處理。
2.特點
-
所有代碼都運行在模塊作用域,不會污染全局作用域;
-
模塊可以多次加載,但是只會在第一次加載時運行一次,然后運行結果就被緩存了,以后再加載,就直接讀取緩存結果。要想讓模塊再次運行,必須清除緩存;
-
模塊加載的順序,按照其在代碼中出現的順序;
3.基本語法
-
暴露模塊:
module.exports = value
或exports.xxx = value
-
引入模塊:
require(xxx)
,如果是第三方模塊,xxx為模塊名;如果是自定義模塊,xxx為模塊文件路徑
// example.js
var x = 5;
var addX = function (value) {return value + x;
};
module.exports.x = x;
module.exports.addX = addX;引入:var example = require('./example.js');//如果參數字符串以“./”開頭,則表示加載的是一個位于相對路徑
console.log(example.x); // 5
console.log(example.addX(1)); // 6
4.模塊的加載機制
????????CommonJS模塊的加載機制是,輸入的是被輸出的值的拷貝。也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。這點與ES6模塊化有重大差異。
// example.js
var x = 5;
var addX = function (value) {return value + x;
};
module.exports.x = x;
module.exports.addX = addX;引入:var example = require('./example.js');//如果參數字符串以“./”開頭,則表示加載的是一個位于相對路徑
console.log(example.x); // 5
console.log(example.addX(1)); // 6
二. AMD
1.概念
????????AMD異步模塊定義,AMD 模塊實現的核心是用函數包裝模塊定義。這樣可以防止聲明全局變量,并允許加載器庫控制 何時加載模塊。包裝模塊的函數是全局 define 的參數,它是由 AMD 加載器庫的實現定義的。
// dataService.js文件
// 定義沒有依賴的模塊
define(function() {let msg = 'www.chenghuai.com'function getMsg() {return msg.toUpperCase()}return { getMsg } // 暴露模塊
})//alerter.js文件
// 定義有依賴的模塊
define(['dataService'], function(dataService) {let name = 'chenghuai'function showMsg() {alert(dataService.getMsg() + ', ' + name)}// 暴露模塊return { showMsg }
})// main.js文件
(function() {require.config({baseUrl: 'js/', //基本路徑 出發點在根目錄下paths: {//映射: 模塊標識名: 路徑alerter: './modules/alerter', //此處不能寫成alerter.js,會報錯dataService: './modules/dataService'}})require(['alerter'], function(alerter) {alerter.showMsg()})
})()// index.html文件
<!DOCTYPE html>
<html><head><title>Modular Demo</title></head><body><!-- 引入require.js并指定js主文件的入口 --><script data-main="js/main" src="js/libs/require.js"></script></body>
</html>
AMD 也支持 require 和 exports 對象,通過它們可以在 AMD 模塊工廠函數內部定義 CommonJS
風格的模塊。這樣可以像請求模塊一樣請求它們,但 AMD 加載器會將它們識別為原生 AMD 結構,而不是模塊定義:

三. UMD
1.概念
????????為了統一 CommonJS 和 AMD 生態系統,通用模塊定義( UMD , Universal Module Definition )規范 應運而生。UMD 可用于創建這兩個系統都可以使用的模塊代碼。本質上, UMD 定義的模塊會在啟動時 檢測要使用哪個模塊系統,然后進行適當配置,并把所有邏輯包裝在一個立即調用的函數表達式(IIFE ) 中。雖然這種組合并不完美,但在很多場景下足以實現兩個生態的共存。
(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD。注冊為匿名模塊define(['moduleB'], factory); } else if (typeof module === 'object' && module.exports) { // Node。不支持嚴格 CommonJS // 但可以在 Node 這樣支持 module.exports 的// 類 CommonJS 環境下使用module.exports = factory(require(' moduleB ')); } else { // 瀏覽器全局上下文(root 是 window)root.returnExports = factory(root. moduleB); }
}(this, function (moduleB) { // 以某種方式使用 moduleB // 將返回值作為模塊的導出// 這個例子返回了一個對象// 但是模塊也可以返回函數作為導出值return {};
}));
四.?ES6 模塊
1. 模塊標簽及定義
????????ECMAScript 6 模塊是作為一整塊 JavaScript 代碼而存在的。帶有 type="module" 屬性的 <script> 標簽會告訴瀏覽器相關代碼應該作為模塊執行,而不是作為傳統的腳本執行。模塊可以嵌入在網頁中, 也可以作為外部文件引入:

????????與傳統腳本不同,所有模塊都會像<script defer> 加載的腳本一樣按順序執行。解析到 <script
type="module"> 標簽后會立即下載模塊文件,但執行會延遲到文檔解析完成。無論對嵌入的模塊代碼, 還是引入的外部模塊文件,都是這樣。<script type="module"> 在頁面中出現的順序就是它們執行 的順序。與<script defer> 一樣,修改模塊標簽的位置,無論是在 <head> 還是在 <body> 中,只會影 響文件什么時候加載,而不會影響模塊什么時候加載。
下面演示了嵌入模塊代碼的執行順序:

不管它是如何加載的,實際上都只會加載一次,如下面的代碼所示:

ECMAScript 6 模塊借用了 CommonJS 和 AMD 的很多優秀特性。下面簡單列舉一些。
優點:
? 模塊代碼只在加載后執行。
? 模塊只能加載一次。
? 模塊是單例。
? 模塊可以定義公共接口,其他模塊可以基于這個公共接口觀察和交互。
? 模塊可以請求加載其他模塊。
? 支持循環依賴。
ES6 模塊系統也增加了一些新行為。
? ES6 模塊默認在嚴格模式下執行。
? ES6 模塊不共享全局命名空間。
? 模塊頂級 this 的值是 undefined (常規腳本中是 window )。
? 模塊中的 var 聲明不會添加到 window 對象。
? ES6 模塊是異步加載和執行的。
2.模塊導出
????????ES6 模塊的公共導出系統與 CommonJS 非常相似。控制模塊的哪些部分對外部可見的是 export 關 鍵字。ES6 模塊支持兩種導出:命名導出和默認導出。不同的導出方式對應不同的導入方式。

????????導出時也可以提供別名,別名必須在 export 子句的大括號語法中指定。因此,聲明值、導出值和 為導出值提供別名不能在一行完成。在下面的例子中,導入這個模塊的外部模塊可以使用 myFoo 訪問 導出的值:

????????默認導出 ( default export )就好像模塊與被導出的值是一回事。默認導出使用 default 關鍵字將一 個值聲明為默認導出,每個模塊只能有一個默認導出。


3.模塊導入
????????模塊可以通過使用 import 關鍵字使用其他模塊導出的值。與 export 類似, import 必須出現在
模塊的頂級:

????????導入對模塊而言是只讀的,實際上相當于 const 聲明的變量。在使用 * 執行批量導入時,賦值給別 名的命名導出就好像使用 Object.freeze() 凍結過一樣。直接修改導出的值是不可能的,但可以修改 導出對象的屬性。同樣,也不能給導出的集合添加或刪除導出的屬性。要修改導出的值,必須使用有內 部變量和屬性訪問權限的導出方法。

????????命名導出和默認導出的區別也反映在它們的導入上。命名導出可以使用* 批量獲取并賦值給保存導 出集合的別名,而無須列出每個標識符:
