細說JS模塊化規範(CommonJS、AMD、CMD、ES6 Module)

這是我參與8月更文挑戰的第8天,活動詳情查看:8月更文挑戰前端


模塊化

模塊化及其優勢

模塊化就是將系統分離成獨立功能的模塊,這樣咱們須要什麼功能,就加載什麼功能。web

模塊化的優勢編程

  • 避免命名空間的衝突(減小命名空間的污染)
  • 更好的分離,實現按需加載
  • 提升可代碼的複用性
  • 提升了代碼的維護性

JS模塊化大體發展過程

CommonJS(服務端) =>AMD(瀏覽器) => CMD => ES6 Module模塊化瀏覽器

模塊化規範的種類

image.png

模塊化規範的發展趨勢

image.png 前端模塊規範有三種:CommonJs,AMD和CMD。
CommonJs用在服務器端,AMD和CMD用在瀏覽器環境
AMD 是 RequireJS 在推廣過程當中對模塊定義的規範化產出。
CMD 是 SeaJS 在推廣過程當中對模塊定義的規範化產出。\緩存

AMD:提早執行(異步加載:依賴先執行)+延遲執行
CMD:延遲執行(運行到需加載,根據順序執行)服務器

CommonJS 規範

CommonJS 的概念

Node 應用由模塊組成,採用 CommonJS 模塊規範。markdown

每一個文件就是一個獨立模塊有本身的做用域。在一個文件裏面定義的變量、函數、類,都是私有的,對其餘文件不可見。網絡

  • 每一個文件均可以做爲一個模塊(這裏的文件指的是js文件)
  • 在服務器端:模塊的加載是運行時同步加載的
  • 在瀏覽去端:模塊須要提早編譯打包處理,否則瀏覽器不能識別require語法

CommonJS 的使用

主要分爲定義模塊和引入模塊兩個步驟。異步

定義模塊模塊化

CommonJS規範規定,每一個模塊內部,module變量表明當前模塊。這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。加載某個模塊,實際上是加載該模塊的module.exports屬性。

// 經過`module.exports`輸出變量`x`和函數`addX`
let x = 1;
let addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
複製代碼

引入模塊

require方法用於加載模塊。

var example = require('./example.js');
複製代碼

CommonJS 的特色

  • 全部代碼都運行在模塊做用域,不會污染全局做用域。
  • 模塊能夠屢次加載,可是隻會在第一次加載時運行一次,而後運行結果就被緩存了,之後再加載,就直接讀取緩存結果。要想讓模塊再次運行,必須清除緩存。
  • 模塊加載是一項阻塞操做,也就是同步加載。模塊加載的順序,按照其在代碼中出現的順序。

模塊的加載機制

CommonJS模塊的加載機制是,輸入的是被輸出的值的拷貝。也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。

AMD 規範

ADM 概念

AMD規範全稱是Asynchronous Module Definition,即異步模塊加載機制,主要用於瀏覽器。它完整描述了模塊的定義,依賴關係,引用關係以及加載機制。因爲該規範不是原生js支持的,使用AMD規範進行開發的時候須要引入第三方的庫函數,AMD對應的就是鼎鼎大名的RequireJS

  • CommonJS規範出現後,在Node開發中產生了很是好的效果,開發者但願借鑑這個經驗來解決瀏覽器JS的模塊化
  • 可是大部分人認爲瀏覽器和服務器的環境差異太大,畢竟瀏覽器JS是經過網絡動態以此加載的,而服務器的JS是保存在本地磁盤中。所以瀏覽器須要實現異步加載,模塊在定義的時候就必須先知名它所須要依賴的模塊,而後把本模塊的代碼寫在回調函數中執行,最終衍生出了AMD規範
  • AMD的主要思想時異步模塊,主邏輯在函數回調中執行。

AMD 的使用

define和require這兩個定義模塊、調用模塊的方法,合稱爲AMD模式。它的模塊定義的方法很是清晰,不會污染全局環境,可以清楚地顯示依賴關係。

使用define用來暴露模塊,使用require用來引入模塊;require()異步加載模塊,瀏覽器不會失去響應;它指定的回調函數,只有前面的模塊都加載成功後,纔會運行,解決了依賴性的問題。

AMD模式能夠用於瀏覽器環境,而且容許異步加載模塊,也能夠根據須要動態加載模塊。

define方法:定義模塊

define方法用於定義模塊,RequireJS要求每一個模塊放在一個單獨的文件裏。 按照是否依賴其餘模塊,能夠分紅兩種狀況討論。第一種狀況是定義獨立模塊,即所定義的模塊不依賴其餘模塊;第二種狀況是定義非獨立模塊,即所定義的模塊依賴於其餘模塊。

require方法:調用模塊

require方法用於調用模塊。它的參數與define方法相似。

RequireJS主要解決了兩個問題:

  1. 多個js文件可能有依賴關係,被依賴的文件須要早於依賴它的文件加載到瀏覽器
  2. js加載的時候瀏覽器會中止頁面渲染,加載文件越多,頁面失去響應時間越長

AMD規範與CommonJS規範的兼容性

CommonJS規範加載模塊是同步的,也就是說,只有加載完成,才能執行後面的操做。AMD規範則是非同步加載模塊,容許指定回調函數。因爲Node.js主要用於服務器編程,模塊文件通常都已經存在於本地硬盤,因此加載起來比較快,不用考慮非同步加載的方式,因此CommonJS規範比較適用。可是,若是是瀏覽器環境,要從服務器端加載模塊,這時就必須採用非同步模式,所以瀏覽器端通常採用AMD規範。

CMD 規範

CMD 的概念

CMD(Common Module Definition) ,通用模塊定義,它解決的問題和AMD規範是同樣的,只不過在模塊定義方式和模塊加載時機上不一樣,CMD也須要額外的引入第三方的庫文件,SeaJS,SeaJS推崇一個模塊一個文件

  • AMD/RequireJS的JS模塊實現有不少不優雅的地方,主要緣由不能以一種更好的管理模塊的依賴加載和執行;
  • 那麼就出現了SeaJS,SeaJs遵循的是CMD規範,CMD規範在AMD的基礎上改進的一種規範,解決了AMD對依賴模塊的執行時機的問題;
  • SeaJS模塊化的順序是:模塊化預加載=》主邏輯調用模塊時才執行模塊中的代碼;
  • SeaJS的用法和AMD基本相同,而且融合了CommonJS的寫法;

CMD 的使用

(對於模塊的引入,具備同步和異步兩中方式)

define 是一個全局函數,用來定義模塊

SeaJS提供了seajs.use來加載模塊

SeaJS的出現,是CommonJS在瀏覽器的踐行者,並吸取了RequireJS的優勢

ES6中的Module模塊

僅支持靜態導入導出

ES6 規範只支持靜態的導入和導出,ES Module 須要在編譯時期進行模塊靜態優化,也就是必需要在編譯時就能肯定。

爲何要這麼作,主要是兩點:

  1. 性能,在編譯階段即完成全部模塊導入,若是在運行時進行會下降速度
  2. 更好的檢查錯誤,好比對變量類型進行檢查

ES6 Module 使用

  • 模塊功能主要由兩個命令構成:exportimport
  • export用於暴露接口,import用於引入模塊

在使用 ES Module 值得注意的是:import 和 export 命令只能在模塊的頂層,在代碼塊中將會報錯,這是由於 ES Module 須要在編譯時期進行模塊靜態優化,import 和 export 命令會被 JavaScript 引擎靜態分析,先於模塊內的其餘語句執行,這種設計有利於編譯器提升效率,但也致使沒法在運行時加載模塊(動態加載)。

對於這個缺點,TC39 有了一個新的提案 – Dynamic Import,提案的內容是建議引入 import()方法,實現模塊動態加載。

// specifier: 指定所要加載的模塊的位置
import(specifier)
複製代碼
  • import()函數能夠用在任何地方,不只僅是模塊,非模塊的腳本也可使用。

它是運行時執行,也就是說,何時運行到這句話,就會加載到指定的模塊。另外,import()函數所加載的模塊沒有靜態連接關係,這點也是與import語法不一樣

  • 注意的時ES6 的Module語法有些瀏覽器是不支持的,所以須要Babel先進性轉碼,將import和export命令轉成ES5語法才能被瀏覽器解析。

CommonJS、AMD、CMD、ES6 Module的區別

AMD與CMD區別

  1. 模塊定義時對依賴的處理不一樣

    AMD推崇前置依賴,在定義模塊時就要聲明其依賴的模塊;而CMD推從就近依賴,只有在用到某個模塊時再使用require導入;

  2. 對依賴模塊的處理機制不一樣

    首先AMD和CMD對模塊的加載方式都是異步的 不過區別在於AMD當加載了依賴模塊以後當即執行依賴模塊,依賴模塊的執行順序和咱們書寫的順序不必定一致; 而CMD加載完依賴模塊以後,並不會當即執行,等全部的依賴模塊都加載好以後,進入回到函數邏輯,遇到require語句的時候,才執行對應的模塊,這樣模塊的執行順序就和咱們書寫的時候一致了

ES6模塊與CommonJS模塊加載的區別

CommonJS時運行時加載,由於ComminJS加載是先加載整個模塊,生成一個對象(這個對象包含了path這個模塊的全部API),而後再從這個對象上面讀取方法-----運行時加載 ES6是編譯時加載ES6模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態定義階段就會生成-----編譯時加載

//ES6模塊
import { basename, dirname, parse } from 'path';

//CommonJS模塊
let { basename, dirname, parse } = require('path');
複製代碼

以上這種寫法與CommonJS的模塊加載有什麼不一樣?

  • 當require path 時,CommonJS會將path模塊運行一遍,並返回一個對象,這個對象包含path模塊的全部API。

總結

模塊化就是將系統分離成獨立功能的模塊,這樣咱們須要什麼功能,就加載什麼功能。

模塊化的優勢

  • 避免命名空間的衝突(減小命名空間的污染)
  • 更好的分離,實現按需加載
  • 提升可代碼的複用性
  • 提升了代碼的維護性

JS模塊化發展產物:CommonJS(服務端) =>AMD(瀏覽器) => CMD => ES6 Module模塊化

CommonJs主要用在服務器端,AMD和CMD用在瀏覽器環境 AMD 是 RequireJS 在推廣過程當中對模塊定義的規範化產出。 CMD 是 SeaJS 在推廣過程當中對模塊定義的規範化產出。 ES6 規範只支持靜態的導入和導出,與CommonJS 的運行時加載不一樣的是ES6是編譯時加載,ES Module 是在編譯時期進行模塊靜態優化。


若是這篇文章幫到了你,記得點贊👍收藏加關注哦😊,但願點贊多多多多...

文中若有錯誤,歡迎在評論區指正

相關文章
相關標籤/搜索