本文將從以下三點來做一個詳細講解:
- 模組化產生
- 早期模組化解決方案
- 模組化規範的產生和差異
模組化產生
在早期的前端開發中,並沒有模組的概念,模組只在服務端存在,用於處理複雜的業務通訊等。 直到 AJAX 被提出,前端能夠像後端請求資料,前端邏輯越來越複雜,就出現了許多問題:全域性變數,函式名衝突,依賴關係不好處理... 隨著業務邏輯的增加,對模組需求越來越大,所以才有了後續一系列 AMD、commonJS、ES6Module 規範。
早期模組化解決方案
兩種解決方法:
- 匿名函式自呼叫(閉包): 形成私有變數; 棧記憶體處理
- 基於物件進行分組 堆記憶體處理; “單例模式思想” : 基於單獨的例項, 來實現資訊分組, 避免全域性變數的汙染
下面一個簡單的例子
// 新聞板塊 let newMOdel = (function(){ let time = new Date() const query = function query() {} const handle = function handel() {} return { query, handle } }()) // 皮膚板塊 let skinModel = (function() { let time = '2021-07-05' const hanle = function handel(){ } newMOdel.query() }())
最早期的模組程式設計思想就是這種 “高階單例設計模式”「閉包+物件」的組合 模組塊化程式設計思想:就是各個板塊/模組 /功能 拼接成一起的東西 ,提出公共的模組。
帶來的好處?
公用&複用性、提供開發效率、方便管理、團隊協作開發 ; 問題:需要自己構建、根據模組間的依賴關係,需要明確匯出順序。 所以就產生了一些其他的模組化規範。
模組化規範 - AMD
AMD 即 Asynchronous Module Definition:非同步模組載入,代表 require.js
RequireJS是一個遵守AMD規範的工具庫,用於客戶端的模組管理。它通過 define 方法,將程式碼定義為模組;通過 require 方法,實現程式碼的模組載入,使用時需要下載和匯入項
檔案目錄
├── AMD
├── moduleA.js
├── moduleB.js
├── main.js
└── require.min.js
簡單實現一個require.js
let factories = {} function define(moduleName,factory) { factories[moduleName] = factory } function require(modules,callback) { modules = modules.map(function(item){ let factory = factories[item]; // 定義好每一個 然後把它執行 return factory() // 執行之後返回的東西 放到modules }); callback(...modules) // 然後回掉函式執行這些modules } /**使用AMD */ define('moduleA', function() { return { fn() { console.log('moduleA') } } }); define('moduleB', function() { return { fn() { console.log('moduleB') } } }); require(['moduleB','moduleA'],function(moduleB,moduleA) { moduleB.fn() moduleA.fn() })
模組化規範 - CMD
CMD即 Common Module Definition : 通用模組載入。
CMD(Sea.js )& CommonJs規範(Node.js)。
問題:CommonJs只能在 node 環境下支援,客戶端/瀏覽器不支援 CommonJS 規範
那如何讓瀏覽器支援CommonJs規範? 所以有了 Sea.js ,也就產生了CMD規範(Sea.js 就是Commonjs規範直接搬到瀏覽器上 )
AMD、CMD 區別
AMD 是 RequireJS 在推廣過程中對模組定義的規範化產出 CMD是SeaJS在推廣過程中對模組化定義的規範化產出
區別:
- 對於依賴的模組,AMD 是提前執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。CMD 推崇 as lazy as possible.
- CMD 推崇依賴就近,AMD 推崇依賴前置。
ModuleA.js
const sum = function sum(...args){ let len = args.length let firstItem = args[0] if(len === 0) return 0; if(len === 1) return firstItem; return args.reduce((total,item) => { return total + item }) } export default { sum }
moduleB.js
import A from './a.js' const average = function average(...args) { let len = args.length let firstItem = args[0] if(len === 0) return 0; if(len === 1) return firstItem; return (A.sum(...args) / args.length).toFixed(2) } export default { average }
main.js
import A from './a.js' import B from './b.js' console.log(A.sum(1,2,3)) console.log(B.average(1,2,3))
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script type="module" src="./main.js"></script> </body> </html>
注意:
- es6Module 不需要引入外部依賴,瀏覽器可以直接執行,但要告訴瀏覽器我是esModule規範,type='module' <script type="module" src="./main.js"></script>
- es6Module 瀏覽器不支援file協議,要在本地起一個服務,可以在vscode上裝一個live serve外掛,給本地一個服務。
模組化規範 - ES6Module
ES6 在語言標準的層面上,實現了模組功能,而且實現得相當簡單,完全可以取代 CommonJS 和 AMD 規範,成為瀏覽器和伺服器通用的模組解決方案。
特點:webpack 支援、瀏覽器也支援.
CommonJS、ES6Module差異
- CommonJs輸出的是一個值的拷貝,ES6輸出的是值的引用
- CommonJs是執行時載入,ES Module是編譯時就確認了依賴關係
第二個差異是因為 CommonJS 載入的是一個物件(即module.exports屬性),該物件只有在指令碼執行完才會生成。而 ES6 模組不是物件,它的對外介面只是一種靜態定義,在程式碼靜態解析階段就會生成。