本文將從以下三點來做一個詳細講解:
- 模塊化產生
- 早期模塊化解決方案
- 模塊化規范的產生和差異
模塊化產生
在早期的前端開發中,並沒有模塊的概念,模塊只在服務端存在,用於處理復雜的業務通信等。 直到 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 推崇依賴前置。
模塊化規范 - ES6Module
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插件,給本地一個服務。
ES6 在語言標准的層面上,實現了模塊功能,而且實現得相當簡單,完全可以取代 CommonJS 和 AMD 規范,成為瀏覽器和服務器通用的模塊解決方案。
特點:webpack 支持、瀏覽器也支持.
CommonJS、ES6Module差異
- CommonJs輸出的是一個值的拷貝,ES6輸出的是值的引用
- CommonJs是運行時加載,ES Module是編譯時就確認了依賴關系
第二個差異是因為 CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完才會生成。而 ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。