js中common.js和ECMAScript.js區(qū)別對比分析
以下是關(guān)于 CommonJS 和 ECMAScript Modules(ESM)的詳細對比分析,包含底層原理和示例說明:
?? 核心差異對比表
特性 | CommonJS | ES Modules |
---|---|---|
來源 | Node.js 社區(qū)規(guī)范 | ECMAScript 語言標準 |
加載方式 | 動態(tài)加載(運行時解析) | 靜態(tài)加載(編譯時解析) |
加載環(huán)境 | Node.js 原生支持 | 瀏覽器原生支持,Node.js需開啟 --experimental-modules (v13.2+已穩(wěn)定) |
語法格式 | require() / module.exports | import / export |
加載行為 | 同步加載 | 異步加載 |
模塊解析 | 文件路徑需完整 | 支持 bare module 說明符(需要導入映射) |
變量訪問 | 修改原始導出對象 | 綁定只讀引用 |
循環(huán)引用處理 | 部分加載(未完成的狀態(tài)) | 引用預解析(存在TDZ) |
頂層作用域 | 模塊內(nèi)this 指向module.exports | 頂層this 為undefined |
靜態(tài)分析 | 不支持 Tree-shaking | 支持 Tree-shaking 優(yōu)化 |
?? 底層加載機制差異(圖示)
CommonJS 運行時解析流程
1. 執(zhí)行代碼 → 2. 構(gòu)建模塊對象 → 3. 按需加載依賴 → 4. 包裹成函數(shù)執(zhí)行
Module Wrapper 偽代碼:
function (exports, require, module, __filename, __dirname) { // 用戶代碼在此執(zhí)行 module.exports = ...; }
ESM 預解析流程
1. 解析階段 → 2. 建立模塊關(guān)系圖 → 3. 編譯階段 → 4. 實例化 → 5. 執(zhí)行代碼
關(guān)鍵特性:
- 模塊記錄(Module Record):存儲導入/導出關(guān)系
- 實時綁定(Live Bindings):導出值變化會同步到導入方
??? 代碼示例對比
模塊導出差異
// CommonJS 動態(tài)修改 exports.a = 1; // ? { a: 1 } module.exports = { b: 2 }; // 最終導出 { b: 2 } // ESM 綁定不可變 export let count = 0; export function increment() { count++; // 所有導入模塊都會看到更新后的值 }
循環(huán)依賴處理
// commonjs/a.js console.log('a開始'); exports.done = false; const b = require('./b'); // 此時b尚未完成加載 console.log('在a中,b.done =', b.done); exports.done = true; console.log('a結(jié)束'); // commonjs/b.js console.log('b開始'); exports.done = false; const a = require('./a'); // 此時a導出{done: false} console.log('在b中,a.done =', a.done); exports.done = true; console.log('b結(jié)束');
# 執(zhí)行結(jié)果: a開始 → b開始 → 在b中,a.done = false → b結(jié)束 → 在a中,b.done = true → a結(jié)束
? 現(xiàn)代項目中的互操作性
混合使用解決方案
// 在 ESM 中引入 CJS import cjsModule from './commonjs-module.cjs'; // 在 CJS 中引入 ESM(需異步) const esModule = await import('./es-module.mjs');
Package.json 配置
{ "type": "module", // 默認使用ESM "main": "./index.cjs", // CJS入口 "exports": { "import": "./esm/index.js", // ESM入口 "require": "./cjs/index.js" // CJS入口 } }
?? 轉(zhuǎn)譯工具處理原理(以Babel為例)
# 轉(zhuǎn)換步驟示例 ESM → 解析為AST → 檢測import/export → 替換為require語法 → 添加helper函數(shù)
示例轉(zhuǎn)換效果:
// 原始ESM import { readFile } from 'fs'; export const data = readFile('./file.txt'); // 轉(zhuǎn)換后CommonJS const { readFile } = require('fs'); exports.data = readFile('./file.txt');
?? 性能優(yōu)化差異
CommonJS 優(yōu)化難點
- 無法預知依賴關(guān)系,阻礙并行加載
- 動態(tài)表達式導致死代碼難以消除
require(condition ? 'a' : 'b'); // 無法靜態(tài)分析
ESM 優(yōu)化空間
// webpack利用靜態(tài)分析實現(xiàn)的特性 import(/* webpackPrefetch: true */ './chart'); // 預取 import(/* webpackChunkName: "utils" */ './utils'); // 分塊命名
?? 瀏覽器支持情況
瀏覽器 | ESM支持版本 |
---|---|
Chrome | 61+ |
Firefox | 60+ |
Safari | 10.1+ |
Edge | 16+ |
<!-- 瀏覽器直接使用ESM --> <script type="module" src="app.js"></script>
?? 選用建議
Node.js 服務端
- 新項目 > Node 14:優(yōu)先使用ESM
- 舊項目遷移:逐步替換關(guān)鍵模塊
前端工程
- 統(tǒng)一使用ESM(配合webpack等打包工具)
- 第三方庫需提供ESM版本(通過
package.json
的module
字段)
工具庫開發(fā)
# 推薦雙模式發(fā)布 lib/ ├── esm/ # ESM版本(支持Tree-shaking) ├── cjs/ # CommonJS版本 └── index.d.ts # 類型聲明
兩種模塊系統(tǒng)在JavaScript生態(tài)中仍將長期共存,理解其底層機制有助于更高效地處理模塊化問題。隨著Node.js對ESM支持的完善,未來ESM會成為主流選擇,但CommonJS仍將在老項目中持續(xù)存在。
到此這篇關(guān)于js中common.js和ECMAScript.js區(qū)別的文章就介紹到這了,更多相關(guān)js common.js和ECMAScript.js內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS循環(huán)中正確使用async、await的姿勢分享
async?/?await是ES7的重要特性之一,也是目前社區(qū)里公認的優(yōu)秀異步解決方案,下面這篇文章主要給大家介紹了關(guān)于JS循環(huán)中正確使用async、await的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2021-12-12JavaScripts數(shù)組里的對象排序的24個方法(最新整理收藏)
文章介紹了24種在JavaScript中對數(shù)組進行排序的方法,每種方法都有具體的示例和應用場景,適合不同情況下的排序需求,感興趣的朋友跟隨小編一起看看吧2025-01-01