JavaScript 模塊系統:CJS/AMD/UMD/ESM


文章目錄

  • 前言
  • 一、CommonJS (CJS) - Node.js 的同步模塊系統
    • 1.1 設計背景
    • 1.2 瀏覽器兼容性問題
    • 1.3 Webpack 如何轉換 CJS
    • 1.4 適用場景
  • 二、AMD (Asynchronous Module Definition) - 瀏覽器異步加載方案
    • 2.1 設計背景
    • 2.2 為什么現代瀏覽器不原生支持 AMD
    • 2.3 Webpack/Rollup 如何處理 AMD
    • 2.4 適用場景
  • 三、UMD (Universal Module Definition) - 兼容瀏覽器 + Node.js 的"縫合怪"
    • 3.1 設計背景
    • 3.2 為什么 UMD 代碼看起來這么"丑"
    • 3.3 構建工具如何生成 UMD
    • 3.4 適用場景
  • 四、ES Modules (ESM) - 現代 JavaScript 標準
    • 4.1 設計背景
    • 4.2 為什么舊瀏覽器不支持 ESM
    • 4.3 構建工具如何處理 ESM
    • 4.4 ESM 模塊生命周期的三個階段
    • 4.5 適用場景
  • 五、模塊系統對比總結
  • 總結


前言

模塊系統是 JavaScript 生態演化的核心部分,不同的模塊規范(CJS/AMD/UMD/ESM)針對不同的運行環境設計,它們的加載機制、語法規則和構建工具處理方式都有顯著差異。本文將結合具體案例,詳細解析它們的設計初衷、運行環境適配、構建工具轉換規則,并解釋為什么需要不同的打包策略。


一、CommonJS (CJS) - Node.js 的同步模塊系統

1.1 設計背景

  1. 目標環境:Node.js(服務器端)
  2. 核心需求:同步加載模塊,無需考慮網絡延遲。(設計初衷是 Node.js 的本地文件系統)
  3. 關鍵特性:
    require() 同步阻塞:模塊立即執行。
    module.exports 導出模塊:用于模塊化開發。
    模塊緩存:相同路徑的 require() 只執行一次。

1.2 瀏覽器兼容性問題

  • 為什么瀏覽器不能直接運行 CJS?
// 瀏覽器直接運行會報錯!
const fs = require('fs'); // Uncaught ReferenceError: require is not defined

原因:

  1. API 不兼容:require 是 Node.js 的 API,瀏覽器沒有實現。
  2. 加載方式差異:CJS 是同步加載,瀏覽器需要異步加載(否則阻塞渲染)。

1.3 Webpack 如何轉換 CJS

// 原始代碼 (CJS)
const lodash = require('lodash');
// Webpack 轉換后(簡化版)
const __webpack_modules__ = {'lodash': (module) => { module.exports = _; }
};
function __webpack_require__(moduleId) {// 1. 檢查緩存// 2. 執行模塊代碼// 3. 返回 module.exports
}
const lodash = __webpack_require__('lodash');

關鍵轉換策略

  • 包裹模塊:每個模塊被包裹成函數,避免全局污染。
  • 實現自己的 require 系統:webpack_require 模擬 Node.js 的模塊加載。
  • 依賴分析:構建時靜態分析 require() 調用。

1.4 適用場景

  1. Node.js 后端開發:適用于服務器端的模塊化開發。
  2. 舊版工具鏈:如 Webpack 4 默認使用 CJS。

二、AMD (Asynchronous Module Definition) - 瀏覽器異步加載方案

2.1 設計背景

  1. 目標環境:瀏覽器(RequireJS)
  2. 核心需求:異步加載,避免阻塞渲染
  3. 關鍵特性:
    define() 定義模塊
    require([], callback) 動態加載依賴
    依賴前置:所有依賴必須在回調函數之前聲明

獨立模塊立即執行,依賴模塊按序加載、回調執行

2.2 為什么現代瀏覽器不原生支持 AMD

<!-- 必須手動加載 RequireJS -->
<script src="require.js"></script>
<script>require(['jquery'], function($) {// 回調函數內才能使用 jQuery});
</script>

原因:

  1. AMD 是社區規范,不是 ECMAScript 標準
  2. 現代瀏覽器原生支持 ESM,不再需要 AMD

2.3 Webpack/Rollup 如何處理 AMD

// 原始 AMD 代碼
define(['jquery'], function($) {return { init: () => $('body').css('color', 'red') };
});
// Webpack 轉換后(Promise 化)
__webpack_require__.e("jquery").then(function() {const $ = __webpack_require__("jquery");return { init: () => $('body').css('color', 'red') };
});

關鍵轉換策略:

  • 轉為 Promise 鏈:適配現代異步編程
  • 代碼拆分:動態加載的模塊會被拆分為單獨 chunk

2.4 適用場景

  1. 舊版瀏覽器項目(IE 8+)
  2. 按需加載的復雜 SPA(如 2015 年前的 AngularJS 項目)

三、UMD (Universal Module Definition) - 兼容瀏覽器 + Node.js 的"縫合怪"

3.1 設計背景

  1. 目標環境:同時支持瀏覽器、Node.js、AMD
  2. 核心需求:一份代碼,多環境運行(適配所有規范)
  3. 關鍵特性:
    環境嗅探:判斷當前是 CJS/AMD/全局變量
    手動適配:通過 if-else 實現多環境兼容

3.2 為什么 UMD 代碼看起來這么"丑"

// UMD 模板代碼(jQuery 風格)
(function (global, factory) {if (typeof define === 'function' && define.amd) {// AMD 環境define(['jquery'], factory);} else if (typeof exports === 'object') {// CJS 環境 (Node.js)module.exports = factory(require('jquery'));} else {// 瀏覽器全局變量global.myLib = factory(global.jQuery);}
}(typeof self !== 'undefined' ? self : this, function ($) {// 實際模塊代碼return { init: () => $('body').css('color', 'red') };
}));
</script>

原因:

  1. 需要手動判斷運行環境
  2. 必須兼容多種模塊加載方式

3.3 構建工具如何生成 UMD

# Rollup 生成 UMD
rollup -i src/index.js -o dist/bundle.umd.js -f umd -n myLib
// 輸出結構
(function (global, factory) {// 環境檢測邏輯...
})(this, function() {return /* 模塊內容 */;
});

關鍵轉換策略:

  • 包裹 IIFE(Immediately Invoked Function Expression,立即調用函數表達式):避免污染全局作用域
  • 注入環境判斷:運行時動態選擇模塊系統

3.4 適用場景

  1. 開源庫開發(如 Lodash、Moment.js)
  2. 需要同時支持<script>標簽和 npm 安裝的項目
export function kInstallScript(src: string): Promise<void> {return new Promise((resolve, reject) => {const script = document.createElement('script');script.src = src;script.onload = resolve as () => void;script.onerror = reject;document.head.append(script);});
}
  await kInstallScript(kIsMobile? '/lib/mobileFilePreview/file-preview.umd.min.js': '/file-preview/filePreview.umd.min.js');kFilePreviewSDK = kIsMobile? (globalThis as any)['file-preview'].FilePreviewSDK: (globalThis as any).FilePreview;// globalThis 在瀏覽器環境中會將模塊掛載到全局對象(如 window)上,屬性名就是在打包配置中指定的 name
// UMD 模塊的全局變量名

傳統瀏覽器用戶:希望通過<script src="awesome-lib.js">直接使用
現代前端工程:希望通過 npm install awesome-lib 引入

用戶環境不可控,而 UMD 就是最好的兼容方案
當使用 RollupWebpack 之類的打包器時,UMD 通常用作備用模塊

四、ES Modules (ESM) - 現代 JavaScript 標準

4.1 設計背景

  1. 目標環境:現代瀏覽器 + Node.js(ES6+)
  2. 核心需求:官方標準、靜態分析、Tree Shaking
  3. 關鍵特性:
    import / export 語法
    靜態加載:依賴關系在編譯時確定
    原生支持:瀏覽器和 Node.js 均可直接運行

靜態分析(Static Analysis)是編程語言和構建工具在 不實際執行代碼的情況下,通過分析代碼的結構、語法、依賴關系來推導代碼行為的技術。它在 Tree Shaking(搖樹優化)中扮演核心角色,直接影響打包工具的無用代碼消除能力。

靜態分析是 現代前端工具鏈的基石

  • Tree Shaking 能安全刪除未使用代碼
  • 類型系統 能提前發現錯誤
  • 壓縮工具 能極致優化體積

CJS 的動態特性破壞了靜態分析的前提,而 ESM 的嚴格靜態結構讓工具能精確推導代碼行為。這就是為什么現代前端生態(Vite/Rollup/Snowpack)都基于 ESM 設計。

模塊系統靜態分析可行性根本原因
ESM? 完美支持語言標準強制靜態結構
CJS?? 有限支持require() 動態性破壞分析前提
AMD? 基礎支持依賴數組顯式聲明
UMD? 幾乎不可用混合模式導致邏輯分裂
SystemJS? 不可用動態注冊機制

4.2 為什么舊瀏覽器不支持 ESM

<!-- 現代瀏覽器 -->
<script type="module">import { add } from './math.js'; // 正常工作
</script>
<!-- 舊瀏覽器 -->
<script>import { add } from './math.js'; // SyntaxError
</script>

原因:

  1. import/export 是 ES6 語法,IE 11 及更早版本不支持
  2. 傳統 <script> 默認是全局腳本,不解析模塊語法

4.3 構建工具如何處理 ESM

webpack:

// 原始 ESM
import React from 'react';
// Webpack 轉換后(CJS 風格)
const React = __webpack_require__('react');
  • 策略:默認轉為 CJS,兼容舊環境

Vite:

// 開發模式:直接返回 ESM
import React from '/node_modules/react/index.js'; // 瀏覽器發起請求
// 生產模式:Rollup 打包
import { r as React } from './chunk-abc123.js';
  • 策略:
    開發模式:開發時不需要打包,利用瀏覽器原生 ESM
    生產模式:Rollup 打包優化

ESM 的模塊處理機制與傳統 AMD/CJS 有本質區別,主要體現在編譯時靜態分析運行時執行控制兩個階段。

4.4 ESM 模塊生命周期的三個階段

(1) 解析階段(Parsing)
靜態分析所有 import(無論是否會被執行)

// main.js
import { unused } from './unused.js'; // 即使從未使用也會被分析
import { core } from './core.js';
if (false) unused(); // 死代碼

構建不可變的依賴圖:
在這里插入圖片描述
(2) 加載階段(Loading)
瀏覽器/Node.js 的行為:

  • 立即并行請求所有依賴模塊(包括unused.js)
  • 阻塞性:必須所有依賴加載完成才會進入執行階段
示例網絡請求:
GET /main.js
GET /unused.js (并行)
GET /core.js   (并行)

(3) 執行階段(Evaluation)
嚴格按拓撲順序初始化(從葉子節點開始):

執行順序:unused.js → core.js → main.js
關鍵特性:
即使模塊導出未被使用,該模塊仍會被執行(包括其頂層代碼)
但不會執行未被調用的函數

模塊初始化 ≠ 導出被調用
即使導出未被使用,模塊的頂層代碼仍會執行(打包工具可能通過Tree Shaking移除)

4.5 適用場景

  1. 現代前端項目(React/Vue 3+)
  2. Node.js 14+ 后端項目

五、模塊系統對比總結

模塊系統加載方式環境支持構建工具轉換策略典型應用場景
CJS同步Node.js包裹為函數,模擬 requireNode.js 后端
AMD異步瀏覽器 (RequireJS)轉為 Promise + 代碼拆分舊版瀏覽器 SPA
UMD兼容多種瀏覽器 + Node.jsIIFE + 環境嗅探開源庫開發
ESM靜態現代瀏覽器 + Node原生支持或轉為 CJS現代前端/Node 項目
特性ESMAMD/RequireJSCommonJS
依賴獲取時機并行請求所有發現的依賴按需并行請求同步阻塞加載
執行觸發條件整個依賴圖就緒后拓撲序執行串行回調(按照申明順序)遇到 require 時立即執行
循環依賴處理語言標準明確定義(引用未初始化值)依賴加載器實現(可能不一致)部分導出可能為 undefined
典型場景import './module.js'require(['module'], callback)const m = require('./module')

總結

  • CJS:Node.js 專用,同步加載,需構建工具轉換才能在瀏覽器運行
  • AMD:舊瀏覽器異步加載方案,已被 ESM 取代
  • UMD:兼容瀏覽器 + Node.js 的過渡方案,適合庫開發
  • ESM:現代標準,支持 Tree Shaking,未來唯一選擇

構建工具的作用就是抹平環境差異,讓開發者可以用任意模塊規范編寫代碼,最終輸出目標環境可運行的版本。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/907937.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/907937.shtml
英文地址,請注明出處:http://en.pswp.cn/news/907937.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用NMEA Tools生成GPS軌跡圖

前言 在現代科技快速發展的時代&#xff0c;GPS定位技術已經廣泛應用于各種領域&#xff0c;如導航、物流、運動追蹤等。為了獲取和分析GPS數據&#xff0c;我們常常需要使用一些專業的工具。本文將詳細介紹如何使用一款名為“NMEA Tools”的APK應用&#xff0c;結合GPSVisual…

Word雙欄英文論文排版攻略

word寫雙欄英文論文的注意事項 排版首先改字體添加連字符還沒完呢有時候設置了兩端對齊會出現這樣的情況&#xff1a; 公式文獻 等我下學期有時間了&#xff0c;一定要學習Latex啊&#xff0c;word寫英文論文&#xff0c;不論是排版還是公式都很麻煩的&#xff0c;而Latex一鍵就…

FactoryBean 接口

Spring 框架中 FactoryBean 接口的特性&#xff0c;這是 Spring 提供的一種特殊機制&#xff0c;用于創建和管理復雜 Bean。讓我通過示例和解釋幫您理解這個概念。 一、FactoryBean 是什么&#xff1f; FactoryBean 是 Spring 框架提供的一個工廠接口&#xff0c;用于創建復雜…

MySQL + CloudCanal + Iceberg + StarRocks 構建全棧數據服務

簡述 在業務數據快速膨脹的今天&#xff0c;企業對 低成本存儲 與 實時查詢分析能力 的需求愈發迫切。 本文將帶你實戰構建一條 MySQL 到 Iceberg 的數據鏈路&#xff0c;借助 CloudCanal 快速完成數據遷移與同步&#xff0c;并使用 StarRocks 完成數據查詢等操作&#xff0c…

javaEE->多線程:線程池

線程池 線程誕生的意義&#xff1a;因為進程的創建/銷毀&#xff0c;太重量了&#xff08;比較慢&#xff09; 但如果近一步提高創建/銷毀的頻率&#xff0c;線程的開銷也不容忽視。 有兩種方法可以提高效率&#xff1a; 1.協程&#xff08;輕量級線程&#xff09;&#xff…

【001】利用github搭建靜態網站_essay

文章目錄 1. 簡介2. 先了解網址規則2.1 文件及網址形式2.2 相互訪問 3. 搭建網頁的過程3.1 網頁文件3.2 github搭建倉庫及文件上傳3.3 搭建網站 1. 簡介 相信大家都有過想要自己搭建一個穩定可靠的網站&#xff0c;github是一個不錯的選擇&#xff0c;本來國內有gitee可以搭建…

搜索引擎2.0(based elasticsearch6.8)設計與實現細節(完整版)

1 簡介 1.1 背景 《搜索引擎onesearch 1.0-設計與實現.docx》介紹了1.0特性&#xff0c;搜索schema&#xff0c;agg&#xff0c;表達式搜索映射&#xff0c;本文介紹onesearch 2.0 新特性, 參考第2節 規劃特性與發布計劃 1.2 關鍵詞 文檔 Document elasticsearch 一行數據稱為…

【ClickHouse】RollingBitmap

ClickHouse 的 RollingBitmap 是一種基于 Bitmap 的數據結構&#xff0c;用于高效處理數據的動態變化和時間窗口計算。以下是關于 ClickHouse RollingBitmap 的相關信息&#xff1a; RollingBitmap 的應用場景 RollingBitmap 主要用于需要處理動態數據和時間窗口計算的場景&am…

新版智慧社區(小區)智能化弱電系統解決方案

該方案聚焦新版智慧社區智能化弱電系統建設,以物聯網、云計算、AI 人臉識別等技術為支撐,構建涵蓋智能可視化對講、智慧門禁、智能梯控、智慧停車、視頻監控等核心系統的社區智能化體系,并通過智慧社區集成平臺實現設備管理、數據統計、預警聯動等功能。方案旨在解決傳統社區…

11高可用與容錯

一、Broker 高可用架構設計 1.1 RabbitMQ 鏡像集群方案 集群搭建步驟 # 節點1初始化 rabbitmq-server -detached rabbitmq-plugins enable rabbitmq_management# 節點2加入集群 rabbitmqctl stop_app rabbitmqctl join_cluster rabbitnode1 rabbitmqctl start_app# 創建鏡像…

AsyncIOScheduler與BackgroundScheduler的線程模型對比

1. BackgroundScheduler的線程機制? ?多線程模型?&#xff1a;BackgroundScheduler基于線程池執行任務&#xff0c;默認通過ThreadPoolExecutor創建獨立線程處理任務&#xff0c;每個任務運行在單獨的線程中&#xff0c;主線程不會被阻塞。?適用場景?&#xff1a;適合同步…

ceph 對象存儲用戶限額滿導致無法上傳文件

查看日志 kl logs -f rook-ceph-rgw-my-store-a-5cc4c4d5b5-26n6j|grep -i error|head -1Defaulted container "rgw" out of: rgw, log-collector, chown-container-data-dir (init) debug 2025-05-30T19:44:11.573+0000 7fa7b7a6d700

2025-05-31 Python深度學習9——網絡模型的加載與保存

文章目錄 1 使用現有網絡2 修改網絡結構2.1 添加新層2.2 替換現有層 3 保存網絡模型3.1 完整保存3.2 參數保存&#xff08;推薦&#xff09; 4 加載網絡模型4.1 加載完整模型文件4.2 加載參數文件 5 Checkpoint5.1 保存 Checkpoint5.2 加載 Checkpoint 本文環境&#xff1a; Py…

批量導出CAD屬性塊信息生成到excel——CAD C#二次開發(插件實現)

本插件可實現批量導出文件夾內大量dwg文件的指定塊名的屬性信息到excel&#xff0c;效果如下&#xff1a; 插件界面&#xff1a; dll插件如下&#xff1a; 使用方法&#xff1a; 1、獲取此dll插件。 2、cad命令行輸入netload &#xff0c;加載此dll&#xff08;要求AutoCAD&…

在Linux環境里面,Python調用C#寫的動態庫,如何實現?

在Linux環境中&#xff0c;Python可以通過pythonnet&#xff08;CLR的Python綁定&#xff09;或subprocess調用C#動態庫。以下是兩種方法的示例&#xff1a; 方法1&#xff1a;使用pythonnet&#xff08;推薦&#xff09; 前提條件 安裝Mono或.NET Core運行時安裝pythonnet包…

小程序跳轉H5或者其他小程序

1. h5跳轉小程序有兩種情況 &#xff08;1&#xff09;從普通瀏覽器打開的h5頁面跳轉小程序使用wx-open-launch-weapp可以實現h5跳轉小程序 <wx-open-launch-weappstyle"display:block;"v-elseid"launch-btn":username"wechatYsAppid":path…

性能優化 - 案例篇:緩沖區

文章目錄 Pre1. 引言2. 緩沖概念與類比3. Java I/O 中的緩沖實現3.1 FileReader vs BufferedReader&#xff1a;裝飾者模式設計3.2 BufferedInputStream 源碼剖析3.2.1 緩沖區大小的權衡與默認值 4. 異步日志中的緩沖&#xff1a;Logback 異步日志原理與配置要點4.1 Logback 異…

文檔整合自動化

主要功能是按照JSON文件&#xff08;Sort.json&#xff09;中指定的順序合并多個Word文檔&#xff08;.docx&#xff09;&#xff0c;并清除文檔中的所有超鏈接。最終輸出合并后的文檔名為"sorted_按章節順序.docx"。 主要分為幾個部分&#xff1a; 初始化配置 定…

嵌入式(C語言篇)Day13

嵌入式Day13 一段話總結 文檔主要介紹帶有頭指針和尾指針的單鏈表的實現及操作&#xff0c;涵蓋創建、銷毀、頭插、尾插、按索引/數據增刪查、遍歷等核心操作&#xff0c;強調頭插/尾插時間復雜度為O(1)&#xff0c;按索引/數據操作需遍歷鏈表、時間復雜度為O(n)&#xff0c;并…

【ASR】基于分塊非自回歸模型的流式端到端語音識別

論文地址:https://arxiv.org/abs/2107.09428 摘要 非自回歸 (NAR) 模型在語音處理中越來越受到關注。 憑借最新的基于注意力的自動語音識別 (ASR) 結構,與自回歸 (AR) 模型相比,NAR 可以在僅精度略有下降的情況下實現有前景的實時因子 (RTF) 提升。 然而,識別推理需要等待…