JavaScript模块化技术进程详解

很多刚入门前端的小白可能不了解JavaScript模块化技术进程,下面按时间线把 JavaScript 模块化从"无"到"语言原生支持"的完整过程讲清楚,重点讲:为什么出现 → 解决了什么问题 → 语法/原理 → 优缺点 → 现在怎么用


一、无模块时代:全局变量 + 多 script(1995--2009)

1)形态

页面里多个 <script> 按顺序引入:

html 复制代码
<script src="a.js"></script>
<script src="b.js"></script>

所有变量、函数都在 window 全局作用域

2)痛点

  • 全局污染、命名冲突 :两个文件都定义 var name,后面覆盖前面。
  • 依赖顺序硬编码:a 依赖 b,就必须 b 先加载,顺序错直接报错。
  • 无法管理依赖关系:项目一大,依赖全靠人脑维护。

3)雏形:命名空间 + IIFE

命名空间(Namespace)
js 复制代码
// user.js
window.app = window.app || {};
app.user = {
  name: 'Tom',
  getName() { return this.name; }
};

用一个全局对象当"命名空间",减少冲突,但仍然是全局

IIFE(立即执行函数)------真正的"私有作用域"
js 复制代码
// module.js
(function() {
  // 私有,外部访问不到
  let secret = 'xxx';
  function foo() {}

  // 暴露接口到全局
  window.myModule = { foo };
})();

优点:变量私有、不污染全局

缺点:无法声明依赖、不能自动加载依赖


二、CommonJS:服务器端模块化(2009,Node.js)

1)背景

2009 年 Node.js 诞生,JS 进入服务端,急需模块系统,CommonJS 应运而生。

2)核心语法(同步)

js 复制代码
// 导出(module.exports / exports)
// math.js
exports.add = (a, b) => a + b;
module.exports = { add };

// 导入(require)
const math = require('./math');
console.log(math.add(1, 2));

3)核心特点

  • 每个文件就是一个模块,有独立作用域。
  • 运行时解析、同步加载require 是函数,运行时才去读文件、执行。
  • 缓存机制 :第一次加载后缓存,后续 require 直接拿缓存。
  • 循环依赖:部分加载
    • A 依赖 B,B 依赖 A
    • CommonJS 会先导出空对象 ,继续执行,最后补全,可能拿到不完整的对象

4)适用场景 & 局限

  • ✅ 适合服务器端(Node.js):本地文件、IO 快、同步没问题。
  • ❌ 不适合浏览器:同步加载会阻塞页面,不能跨域直接用。

三、AMD:浏览器异步模块(2011,RequireJS)

1)背景

浏览器不能同步阻塞,所以 AMD(Asynchronous Module Definition)主打异步、并行加载

2)核心语法(异步)

js 复制代码
// 定义模块:define(依赖数组, 工厂函数)
// math.js
define([], function() {
  return {
    add(a, b) { return a + b; }
  };
});

// 使用模块
define(['./math'], function(math) {
  console.log(math.add(1, 2));
});

3)特点

  • 异步并行加载:不阻塞页面,依赖并行下载。
  • 依赖前置、提前声明:依赖写在数组里,加载器先加载所有依赖再执行回调。
  • 浏览器优先:RequireJS 是代表。

4)缺点

  • 语法繁琐define + 依赖数组 + 回调
  • 服务端不友好:Node.js 原生不支持,需兼容层。

四、CMD:国内 Sea.js(2012,阿里)

1)定位

CMD(Common Module Definition)是 Sea.js 提的,借鉴 CommonJS 写法、浏览器异步

2)语法(就近依赖)

js 复制代码
// CMD
define(function(require, exports, module) {
  // 依赖就近写
  const math = require('./math');
  exports.add = math.add;
});

3)对比 AMD

  • AMD:依赖前置(先加载所有依赖)
  • CMD:依赖就近(用到再 require)

现在基本被 ESM + 打包工具取代。


五、UMD:通用模块(2014,兼容 CommonJS/AMD)

1)目的

写一个模块,同时支持 CommonJS、AMD、全局变量,适配各种环境。

2)典型模板

js 复制代码
(function(root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define([], factory);
  } else if (typeof module === 'object' && module.exports) {
    // CommonJS
    module.exports = factory();
  } else {
    // 全局
    root.myModule = factory();
  }
})(typeof self !== 'undefined' ? self : this, function() {
  return { add(a,b) {return a+b;} };
});

3)现状

  • 老库常用 UMD,新项目直接 ESM

六、ES Module(ESM):语言原生标准(2015,ES6/ES2015)

1)里程碑

第一次把模块化纳入 JS 语言标准,浏览器和 Node.js 原生支持。

2)语法(静态 import/export)

js 复制代码
// 导出
// math.js
export const add = (a, b) => a + b;
export default { add };

// 导入
import { add } from './math.js';
import math from './math.js';

3)核心特性(对比 CommonJS)

① 静态分析(Tree-Shaking)
  • import 必须在模块顶部,编译阶段就能确定依赖。
  • 打包工具可删除未使用代码(Tree-Shaking),体积更小。
  • CommonJS 是运行时 require无法静态分析、不能 Tree-Shaking
② 异步加载 + 动态导入
  • 浏览器中 <script type="module"> 默认异步、不阻塞
  • 动态导入:import() 函数,按需加载、懒加载
js 复制代码
// 动态导入(运行时)
import('./math.js').then(math => {
  console.log(math.add(1,2));
});
③ 循环依赖处理更优
  • ESM 用绑定(binding)而非拷贝,循环依赖时拿到实时绑定,不是快照。
④ 跨平台统一
  • 浏览器:<script type="module" src="xxx.js">
  • Node.js:.mjspackage.json"type": "module"

4)ESM vs CommonJS 关键对比

特性 CommonJS ES Module(ESM)
加载时机 运行时解析、同步加载 编译时静态分析、异步加载
语法 require / module.exports import / export(静态)
Tree-Shaking ❌ 不支持 ✅ 支持(静态导入)
循环依赖 部分加载(空对象) 实时绑定,更可靠
浏览器原生 ❌ 不支持 ✅ type="module"
Node.js ✅ 默认支持 ✅ .mjs / "type":"module"

七、工程化工具:抹平差异(2012 至今)

1)Browserify(2012)

  • 让浏览器能用 CommonJS,打包成一个文件。
  • 原理:把 require 转成浏览器可执行的函数。

2)Webpack(2014 崛起)

  • 支持 CommonJS + AMD + ESM,统一打包。
  • 核心:依赖图(Dependency Graph),从入口递归找依赖,打包输出。
  • 现在主流:源码写 ESM,Webpack/Rollup/Vite 打包

3)Vite(2020)

  • 浏览器原生 ESM + 按需编译,开发环境极速冷启动

八、总结:模块化演进脉络(一句话)

  1. 1995--2009:无模块 → 全局变量 → IIFE(私有作用域,无依赖管理)。
  2. 2009:CommonJS(Node.js,同步,运行时)。
  3. 2011:AMD(RequireJS,浏览器,异步)。
  4. 2014:UMD(兼容 CommonJS/AMD)。
  5. 2015:ESM(ES6,语言标准,静态+异步,跨平台)。
  6. 2012--now:Browserify → Webpack → Vite(工具抹平差异,统一 ESM)。

本质演进方向
全局 → 私有作用域 → 依赖管理 → 异步加载 → 语言原生标准 → 工程化统一


相关推荐
2zcode1 小时前
基于Matlab元胞自动机模拟(CA)动态再结晶过程
开发语言·matlab·动态再结晶
w_t_y_y1 小时前
VUE组件配置项(二)data和props
前端·javascript·vue.js
Gerardisite1 小时前
企业微信怎么玩?用 API 打造智能私域助手
开发语言·python·机器人·企业微信
buhuizhiyuci1 小时前
【QT-百日筑基篇】打完完怪,开始学炼丹, 前往藏书阁寻找对应材料的信息,并前往去寻找对应材料-QT信号和槽
开发语言·qt
csbysj20201 小时前
Bootstrap5 Jumbotron 深入解析
开发语言
Martin -Tang1 小时前
uniapp+vue3+ts自定义表格
javascript·vue.js·uni-app
郝学胜-神的一滴1 小时前
CMake 010 :一步到位链接静态库
开发语言·c++·qt·程序人生·系统架构·cmake
小则又沐风a1 小时前
C++继承
开发语言·c++
测试员周周1 小时前
【Appium 系列】第10节-手势操作实战 — 滑动、拖拽、缩放与轻拂
linux·服务器·开发语言·人工智能·python·appium·pytest