JS本身簡單的頁面設計:頁面動畫 + 表單提交
並無模組化 or 名稱空間的概念
但是因為JS的模組化需求日益增長
<script src="jquery.js"></script>
<script src="main.js"></script>
<script src="dep1.js"></script>
//……
認可:
檔案分離是最基礎的模組化第一步
問題出現:
栗子:
// 定義一個全域性變數
let count = 0;
// 程式碼塊1
const increase = () => ++count;
// 程式碼塊2
const reset = () => {
count = 0;
}
increase();
reset();
利用函數塊級作用域
(() => {
let count = 0;
// ……
}
僅定義了一個函數,如果立即執行
(() => {
let count = 0;
// ……
}();
初步實現了一個最最最最簡單的模組
嘗試去定義一個最簡單的模組
const iifeModule = (() => {
let count = 0;
return {
increase: () => ++count;
reset: () => {
count = 0;
}
}
})();
iifeModule.increase();
iifeModule.reset();
面試官可能會追問:有額外依賴時,如何優化IIFE相關程式碼
優化1: 依賴其他模組的IIFE
const iifeModule = ((dependencyModule1, dependencyModule2) => {
let count = 0;
return {
increase: () => ++count;
reset: () => {
count = 0;
}
}
})(dependencyModule1, dependencyModule2);
iifeModule.increase();
iifeModule.reset();
面試1:瞭解早期jquery的依賴處理以及模組載入方案嗎?/ 瞭解傳統IIFE是如何解決多方依賴的問題
答:IIFE加傳參調配
實際上,jquery等框架其實應用了revealing的寫法:
揭示模式
const iifeModule = ((dependencyModule1, dependencyModule2) => {
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
}
return {
increase, reset
}
})(dependencyModule1, dependencyModule2);
iifeModule.increase();
iifeModule.reset();
node.js制定
特徵:
模組組織方式
main.js 檔案
// 引入部分
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);
// 處理部分
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
}
// 做一些跟引入依賴相關事宜……
// 暴露介面部分
exports.increase = increase;
exports.reset = reset;
module.exports = {
increase, reset
}
模組使用方式
const { increase, reset } = require('./main.js');
increase();
reset();
可能被問到的問題
實際執行處理
(function (thisValue, exports, require, module) {
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);
// 業務邏輯……
}).call(thisValue, exports, require, module);
- 優點:
CommonJS率先在伺服器端實現了,從框架層面解決依賴、全域性變數汙染的問題
新的問題 —— 非同步依賴
通過非同步載入 + 允許制定回撥函數
經典實現框架是:require.js
新增定義方式:
// 通過define來定義一個模組,然後require進行載入
/*
define
params: 模組名,依賴模組,工廠方法
*/
define(id, [depends], callback);
require([module], callback);
模組定義方式
define('amdModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1, dependencyModule2) => {
// 業務邏輯
// 處理部分
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
}
return {
increase, reset
}
})
引入模組:
require(['amdModule'], amdModule => {
amdModule.increase();
})
面試題2: 如果在AMDmodule中想相容已有程式碼,怎麼辦
define('amdModule', [], require => {
// 引入部分
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);
// 處理部分
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
}
// 做一些跟引入依賴相關事宜……
return {
increase, reset
}
})
面試題3: AMD中使用revealing
define('amdModule', [], (require, export, module) => {
// 引入部分
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);
// 處理部分
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
}
// 做一些跟引入依賴相關事宜……
export.increase = increase();
export.reset = reset();
})
define('amdModule', [], require => {
const otherModule = require('amdModule');
otherModule.increase();
otherModule.reset();
})
面試題4:相容AMD&CJS/如何判斷CJS和AMD
UMD的出現
(define('amdModule', [], (require, export, module) => {
// 引入部分
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);
// 處理部分
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
}
// 做一些跟引入依賴相關事宜……
export.increase = increase();
export.reset = reset();
}))(
// 目標是一次性區分CommonJSorAMD
typeof module === "object"
&& module.exports
&& typeof define !== "function"
? // 是 CJS
factory => module.exports = factory(require, exports, module)
: // 是AMD
define
)
- 優點: 適合在瀏覽器中載入非同步模組,可以並行載入多個模組
按需載入
主要應用的框架 sea.js
define('module', (require, exports, module) => {
let $ = require('jquery');
// jquery相關邏輯
let dependencyModule1 = require('./dependecyModule1');
// dependencyModule1相關邏輯
})
- 優點:按需載入,依賴就近
面試題5:AMD&CMD區別
答:依賴就近,按需載入
走向新時代
新增定義:
引入關鍵字 —— import
匯出關鍵字 —— export
模組引入、匯出和定義的地方:
// 引入區域
import dependencyModule1 from './dependencyModule1.js';
import dependencyModule2 from './dependencyModule2.js';
// 實現程式碼邏輯
let count = 0;
export const increase = () => ++count;
export const reset = () => {
count = 0;
}
// 匯出區域
export default {
increase, reset
}
模板引入的地方
<script type="module" src="esModule.js"></script>
node中:
import { increase, reset } from './esModule.mjs';
increase();
reset();
import esModule from './esModule.mjs';
esModule.increase();
esModule.reset();
面試題6:動態模組
考察:export promise
ES11原生解決方案:
import('./esModule.js').then(dynamicEsModule => {
dynamicEsModule.increase();
})
- 優點(重要性):通過一種最統一的形態整合了js的模組化
根本問題 - 執行時進行依賴分析
前端的模組化處理方案依賴於執行時分析
解決方案:線下執行
grunt gulp webpack
<!doctype html>
<script src="main.js"></script>
<script>
// 給構建工具一個標識位
require.config(__FRAME_CONFIG__);
</script>
<script>
require(['a', 'e'], () => {
// 業務處理
})
</script>
</html>
define('a', () => {
let b = require('b');
let c = require('c');
export.run = () {
// run
}
})
step1: 掃描依賴關係表:
{
a: ['b', 'c'],
b: ['d'],
e: []
}
step2: 重新生成依賴資料模板
<!doctype html>
<script src="main.js"></script>
<script>
// 構建工具生成資料
require.config({
"deps": {
a: ['b', 'c'],
b: ['d'],
e: []
}
})
</script>
<script>
require(['a', 'e'], () => {
// 業務處理
})
</script>
</html>
step3: 執行工具,採用模組化方案解決模組化處理依賴
define('a', ['b', 'c'], () => {
// 執行程式碼
export.run = () => {}
})
優點: