隨著前端js程式碼複雜度的提高,js模組化是必然趨勢,不僅好維護,同時依賴很明確,不會全域性汙染,今天整理一下模組化的幾個規範吧~
無模組化-->CommonJS規範-->AMD規範-->CMD規範-->ES6模組化
1. 無模組化
script標籤引入js檔案,相互羅列,但是被依賴的放在前面,否則使用就會報錯。如下:
<script src="jquery.js"></script>
<script src="jquery_scroller.js"></script>
<script src="main.js"></script>
<script src="other1.js"></script>
<script src="other2.js"></script>
<script src="other3.js"></script>複製程式碼
即簡單的將所有的js檔案統統放在一起。但是這些檔案的順序還不能出錯,比如jquery需要先引入,才能引入jquery外掛,才能在其他的檔案中使用jquery。缺點很明顯:
- 汙染全域性作用域
- 維護成本高
- 依賴關係不明顯
2. CommonJS規範
該規範最初是用在伺服器端的node的,它有四個重要的環境變數為模組化的實現提供支援:module
、exports
、require
、global
。實際使用時,用module.exports
定義當前模組對外輸出的介面(不推薦直接用exports
),用require
載入模組(同步)。
// a-commonJs.js (匯出)
var a = 5;var add = function(param){//在這裡寫上需要向外暴露的函式、變數 return a + param}module.exports.a = a;module.exports.add = add===========================
// b-commonJs.js引用自定義模組,引數包含路徑,可省略.js(匯入)
var addFn = require('./a-commonJs')console.log(addFn.add(3)) //8console.log(addFn.a) //5複製程式碼
一點說明:
exports 是對 module.exports 的引用。比如我們可以認為在一個模組的頂部有這句程式碼: exports = module.exports所以,我們不能直接給exports賦值,比如number、function等。 複製程式碼
注意:因為module.exports本身就是一個物件,所以,我們在匯出時可以使用
module.exports = {foo: 'bar'} //true
module.exports.foo = 'bar' //true
。
但是, exports 是 module.exports 的一個引用,或者理解為exports是一個指標,exports指向module.exports,這樣,我們就只能使用 exports.foo = 'bar'
的方式,而不能使用
exports = {foo: 'bar'} //error 這種方式是錯誤的,相當於重新定義了exports
一點優點:解決了依賴、全域性變數汙染的問題
一點缺點: CommonJS用同步的方式載入模組。在服務端,模組檔案都存在本地磁碟,讀取非常快,所以這樣做不會有問題。但是在瀏覽器端,限於網路原因,CommonJS不適合瀏覽器端模組載入,更合理的方案是使用非同步載入,比如下邊AMD規範。
3. AMD規範
承接上文,AMD規範則是非同步載入模組,允許指定回撥函式,AMD 是 RequireJS 在推廣過程中對模組定義的規範化產出。。
AMD標準中,定義了下面三個API:
require([module], callback)
define(id, [depends], callback)
require.config()
即通過define來定義一個模組,然後使用require來載入一個模組, 使用require.config()指定引用路徑。
先到require.js官網下載最新版本,然後引入到頁面,如下:
<script data-main="./alert" src="./require.js"></script>
data-main屬性不能省略。
以上分別是定義模組,引用模組,執行在瀏覽器彈出提示資訊。
引用模組的時候,我們將模組名放在[]
中作為reqiure()
的第一引數;如果我們定義的模組本身也依賴其他模組,那就需要將它們放在[]
中作為define()
的第一引數。
在使用require.js的時候,我們必須要提前載入所有的依賴,然後才可以使用,而不是需要使用時再載入。
一點優點:適合在瀏覽器環境中非同步載入模組、並行載入多個模組
一點缺點:不能按需載入、開發成本大
4. CMD
AMD 推崇依賴前置、提前執行,CMD推崇依賴就近、延遲執行。CMD 是 SeaJS 在推廣過程中對模組定義的規範化產出。
很明顯,CMD是按需載入,就近原則。
5. ES6模組化
在ES6中,我們可以使用 import 關鍵字引入模組,通過 exprot 關鍵字匯出模組,功能較之於前幾個方案更為強大,也是我們所推崇的,但是由於ES6目前無法在瀏覽器中執行,所以,我們只能通過babel將不被支援的import編譯為當前受到廣泛支援的 require。
es6在匯出的時候有一個預設匯出,export default
,使用它匯出後,在import的時候,不需要加上{},模組名字可以隨意起。該名字實際上就是個物件,包含匯出模組裡面的函式或者變數。
但是一個模組只能有一個export default
。
6. CommonJs和ES6區別
以下引用阮一峰老師的內容:
(1) CommonJS 模組輸出的是一個值的拷貝,ES6 模組輸出的是值的引用。
- CommonJS 模組輸出的是值的拷貝,也就是說,一旦輸出一個值,模組內部的變化就影響不到這個值。
- ES6 模組的執行機制與 CommonJS 不一樣。JS 引擎對指令碼靜態分析的時候,遇到模組載入命令
import
,就會生成一個只讀引用。等到指令碼真正執行時,再根據這個只讀引用,到被載入的那個模組裡面去取值。換句話說,ES6 的import
有點像 Unix 系統的“符號連線”,原始值變了,import
載入的值也會跟著變。因此,ES6 模組是動態引用,並且不會快取值,模組裡面的變數繫結其所在的模組。
(2) CommonJS 模組是執行時載入,ES6 模組是編譯時輸出介面。
-
執行時載入: CommonJS 模組就是物件;即在輸入時是先載入整個模組,生成一個物件,然後再從這個物件上面讀取方法,這種載入稱為“執行時載入”。
-
編譯時載入: ES6 模組不是物件,而是通過
export
命令顯式指定輸出的程式碼,import
時採用靜態命令的形式。即在import
時可以指定載入某個輸出值,而不是載入整個模組,這種載入稱為“編譯時載入”。
CommonJS 載入的是一個物件(即module.exports
屬性),該物件只有在指令碼執行完才會生成。而 ES6 模組不是物件,它的對外介面只是一種靜態定義,在程式碼靜態解析階段就會生成。
一點說明:本文只是自己的一點筆記,方便自己記憶和理解。\(^o^)/~