想象一下你正在寫(xiě)一個(gè)大型項(xiàng)目, 所有代碼都堆在一個(gè)文件里:
var utils = {...};
var api = {...};
var componentA = {...};
var componentB = {...};
function doSomething() {
}
這種寫(xiě)法有幾個(gè)致命問(wèn)題:
命名沖突: 變量和函數(shù)都在全局作用域,容易覆蓋
依賴混亂:不知道代碼執(zhí)行前需要加載哪些依賴
難以維護(hù): 代碼組織像意大利面條,牽一發(fā)而動(dòng)全身
模塊化就是為了解決這些問(wèn)題而生的! 它讓我們可以:
將代碼拆分成獨(dú)立的小文件(模塊)
明確聲明依賴關(guān)系
避免污染全局命名空間
方便代碼復(fù)用和維護(hù)
二、CommonJS
CommonJS 是 Node.js 采用的模塊系統(tǒng), 語(yǔ)法非常簡(jiǎn)單:
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = {
add,
subtract
};
exports.add = add;
exports.subtract = subtract;
const math = require('./math.js');
console.log(math.add(2, 3));
console.log(math.subtract(5, 2));
同步加載: 模塊在 require 時(shí)同步加載并執(zhí)行.
緩存機(jī)制: 模塊首次加載后會(huì)被緩存, 后續(xù) require 直接返回緩存.
值拷貝: 導(dǎo)出的是值的拷貝(對(duì)于原始類(lèi)型), 修改不會(huì)影響原模塊.
三、ES Modules
ES Modules(簡(jiǎn)稱(chēng) ESM)是 ECMAScript 2015(ES6)引入的官方模塊系統(tǒng), 現(xiàn)在已經(jīng)被現(xiàn)代瀏覽器和 Node.js 支持.
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export default {
add,
subtract
};
import { add, subtract } from './math.js';
import math from './math.js';
import math, { add } from './math.js';
import * as math from './math.js';
靜態(tài)分析: ESM 的 import/export 必須在頂層作用域, 可以被靜態(tài)分析
動(dòng)態(tài)綁定: ESM 導(dǎo)出的是值的引用(活的綁定), 修改會(huì)影響所有導(dǎo)入
異步加載: ESM 在瀏覽器中是異步加載的
export let count = 0;
export function increment() {
count++;
}
import { count, increment } from './counter.js';
console.log(count);
increment();
console.log(count);
<script src="app.js"></script>
<script type="module" src="app.js"></script>
關(guān)鍵區(qū)別:
模塊默認(rèn)defer, 在文檔解析完成后執(zhí)行
模塊有獨(dú)立作用域, 不會(huì)污染全局
模塊需要 CORS 配置, 不能使用 file:// 協(xié)議
模塊會(huì)自動(dòng)應(yīng)用嚴(yán)格模式
四、按需加載
靜態(tài)導(dǎo)入(import ... from ...)必須在模塊頂層, 但有時(shí)候我們需要按需加載:
import('./math.js').then(math => {
console.log(math.add(2, 3));
});
async function calculate() {
const math = await import('./math.js');
console.log(math.add(2, 3));
}
if (user.isAdmin) {
import('./admin.js').then(...);
}
<link rel="modulepreload" href="critical-module.js">
Promise.all([
import('./moduleA.js'),
import('./moduleB.js')
]).then(([moduleA, moduleB]) => {
});
閱讀原文:原文鏈接
該文章在 2025/7/21 10:33:09 編輯過(guò)