這一次,我要弄懂javascript的模組化

sunxiaoying123發表於2019-03-04

      隨著前端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的,它有四個重要的環境變數為模組化的實現提供支援:moduleexportsrequireglobal。實際使用時,用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:

  1. require([module], callback)
  2. define(id, [depends], callback)
  3. require.config()

即通過define來定義一個模組,然後使用require來載入一個模組, 使用require.config()指定引用路徑。

先到require.js官網下載最新版本,然後引入到頁面,如下:

<script data-main="./alert" src="./require.js"></script>

data-main屬性不能省略。

這一次,我要弄懂javascript的模組化

這一次,我要弄懂javascript的模組化

以上分別是定義模組,引用模組,執行在瀏覽器彈出提示資訊。

      引用模組的時候,我們將模組名放在[]中作為reqiure()的第一引數;如果我們定義的模組本身也依賴其他模組,那就需要將它們放在[]中作為define()的第一引數。

     在使用require.js的時候,我們必須要提前載入所有的依賴,然後才可以使用,而不是需要使用時再載入。

一點優點:適合在瀏覽器環境中非同步載入模組、並行載入多個模組

一點缺點:不能按需載入、開發成本大

4. CMD

      AMD 推崇依賴前置、提前執行CMD推崇依賴就近、延遲執行。CMD 是 SeaJS 在推廣過程中對模組定義的規範化產出。

這一次,我要弄懂javascript的模組化

很明顯,CMD是按需載入,就近原則。

5. ES6模組化

      在ES6中,我們可以使用 import 關鍵字引入模組,通過 exprot 關鍵字匯出模組,功能較之於前幾個方案更為強大,也是我們所推崇的,但是由於ES6目前無法在瀏覽器中執行,所以,我們只能通過babel將不被支援的import編譯為當前受到廣泛支援的 require。 

這一次,我要弄懂javascript的模組化

      es6在匯出的時候有一個預設匯出,export default,使用它匯出後,在import的時候,不需要加上{},模組名字可以隨意起。該名字實際上就是個物件,包含匯出模組裡面的函式或者變數。

這一次,我要弄懂javascript的模組化

但是一個模組只能有一個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^)/~

相關文章