1. 無模塊化
實現(xiàn):文件分離,順序導入
缺點:污染全局作用域,變量名沖突導致的報錯
2. IIFE (Immediately-invoked Function Expression)
實現(xiàn):利用函數(shù)塊級作用域
const iifeModule = (function(){
let count = 1
function increase () {
return ++count
}
function reset () {
return count = 0
}
return {
increase,
reset
}
})()
iifeModule.increase()
iifeModule.reset()
優(yōu)化IIFE,加入模塊依賴
模塊導入使用 iife 傳參,模塊導出使用 iife 的返回值
const iifeModule = (function(dependency1, dependency2){
let count = 1
function increase () {
return ++count
}
function reset () {
return count = 0
}
return {
increase,
reset
}
})(dependency1, dependency2)
iifeModule.increase()
iifeModule.reset()
Revealing Module Pattern
揭示模式模仿了 OOP 的思想,隱藏私有變量,只暴露公有變量和函數(shù)來操作私有變量
IIFE 優(yōu)點:
- 從語法側解決了全局變量作用域的問題,有了模塊的雛形
IIFE 缺點:
- 多余的語法代碼
- 除了解決作用域的問題,其他的復雜場景問題一概沒有考慮
3. CJS - Commonjs
nodejs 的模塊化方案
每個文件就是一個模塊,有自己的作用域。在一個文件里面定義的變量、函數(shù)、類,都是私有的,對其他文件不可見。
CommonJS規(guī)范規(guī)定,每個模塊內(nèi)部,module變量代表當前模塊。這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。加載某個模塊,其實是加載該模塊的module.exports屬性。
require方法用于加載模塊
const dep1 = require('dep1Module')
const dep2 = require('dep2Module')
// use object dep1 do sth
...
exports.a = a
exports.b = b
// Or
module.exports = {
a, b
}
實現(xiàn)邏輯
使用
(function (require, module, exports) {
// use require
require(...)
// use exports
exports.a = a
// use module.exports
module.exports = {
a
}
})()
// require 模擬
function require(dep) {
// 定義閉包bm'ld
const module = {}
module.exports = {}
const code = file.readFileSync('dep')
// 函數(shù)執(zhí)行完成之后,module.exports 將會被賦值
new Function('require', 'module', 'exports', code)(require, module, exports)
// 導出 dep 的 emodule.exports
return module.exports
}
-
優(yōu)點:
CommonJS 率先在服務端實現(xiàn)了,從框架層面解決全局變量污染、依賴的問題 -
缺點:
主要是服務端的解決方案,不適用于客戶端異步拉取依賴的場景
拋出新的問題 —— 異步依賴
4. AMD (Asynchronous Module Definition)
通過異步加載 + 允許定制回調
經(jīng)典實現(xiàn)框架:require.js
定義方式
/**
* 通過 define 定義一個模塊,然后通過 require 進行加載
* 最后一個參數(shù)是 工廠方法
**/
define(id, [...deps], callback)
require([...module], callback)
模塊的定義
define('myModule', ['dep1', 'dep2'], (dep1, dep2) => {
// 業(yè)務邏輯
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
}
return {
increase, reset
}
})
模塊的引入
require(['myModule'], myModule => {
myModule.increase()
})
- 優(yōu)點:解決了瀏覽器異步加載依賴場景的問題,可以并行加載多個模塊
- 缺點:不能按需加載
5. UMD (Universal Module Definition)
UMD 頂部一般有這樣一段代碼來兼容 CJS 和 AMD
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery', factory])
} else if (typeof 'exports' === 'object') {
// CJS
module.exports = factory(require('jquery'))
} else {
// 將模塊綁定在全局變量上
// 這里的 root.jQuery 也是之前已經(jīng)綁定了的 module
root.myModule = factory(root.jQuery)
}
})(this, function ($) {
// myModule 業(yè)務代碼
return {
a,
b
}
})
- 優(yōu)點:兼容 AMD 和 CJS,可以在雙端運行
- 缺點:未解決 AMD 無法按需加載的問題
6. CMD (Common Module Definition)
主要應用框架 sea.js
require & require.async
- require 導入同步模塊
- require.async 導入異步模塊,第二個參數(shù)是 callback
define('myModule', (require, exports, module) => {
let $ = require('jquery')
// jquery 相關邏輯
...
let dep1 = require.async(['dep1'], dep1 => {
// dep1 module 相關業(yè)務邏輯
...
})
})
- 優(yōu)點:按需加載,同時支持同步和異步 require
- 缺點:依賴打包,加載邏輯存在于每個模塊中,模塊體積擴大
ESM
使用
import 導入模塊
export 導出模塊,export default 導出默認模塊
import dep1 from 'dep1'
// 業(yè)務邏輯
let count = 0
export const increase = () => ++count
export const reset = () => count = 0
export default {
increase, reset
}
模板引入
<script type="module" src="myModule.js"></script>
node 端引入,mjs
import { increase, reset } from './myModule.mjs'
increase()
reset()
動態(tài)導入
ES11 原生解決方案
import('dep').then(dep => {
...
})
- 優(yōu)點:官方提出的統(tǒng)一形態(tài)的模塊化,解決了上面遇到的各種問題
(模塊作用域、依賴導入導出、異步導入等) - 缺點:本質上還是運行時的依賴分析
解決模塊化的新思路 —— 前端工程化
背景
根本問題 —— 運行時的依賴分析
方案:編譯時分析依賴,同時優(yōu)化項目
grunt gulp webpack vite