今天的前端、Node 项目,几乎都会写 import

很多时候,我们甚至已经不太把它当回事。

可如果你退一步看,会发现这件事其实非常奇怪:

为什么一门今天连“引入另一个文件”都看起来理所当然的语言,曾经会为了模块到底该怎么组织,打出一整场十几年都没完全停火的长期战争?

为什么同样都在写 JavaScript,却会先后冒出:

  • 全局变量和命名空间老办法
  • CommonJS
  • AMD
  • CMD
  • bundler 的私家秩序
  • 最后才是标准层的 ES Modules

为什么今天明明都已经有了 ESM,大家的现实世界里却还到处留着:

  • require
  • module.exports
  • 打包产物
  • 双份构建
  • package.json 里的各种格式声明

这套 《模块化江湖》,想讲的就是这些事。

它不是“教你区分 importrequire”的语法课。

也不是“JavaScript 模块发展史速览”的压缩时间线。

它更像一部制度史。

而且是那种特别典型的 JavaScript 制度史:

共同需求明明非常明确,可统一秩序却迟迟来不了;于是社区先各自发明活法,等标准终于赶到时,现实世界早就已经长满了自己的旧账。

你可以先把这套系列记成五幕:

  1. 最早的 JavaScript 没有模块,大家只能靠全局变量、脚本顺序和命名空间硬撑。
  2. Node.js 起来以后,CommonJS 先在服务器和运行时世界里把秩序立住。
  3. 浏览器这边不吃同步加载那一套,于是 AMDRequireJS,再到中文社区里的 CMDSeaJS,都各自长出来。
  4. 当社区格式越长越多,bundler 开始变成临时宪法,先替大家把分裂的现实勉强收拢。
  5. ES Modules 最终进了标准,可它并不是“宣布胜利”的终点,而是和旧世界继续长期磨合的新起点。

这五幕一连起来,你就会发现,这套系列真正想讲的,不是“模块语法终于被发明出来”。

而是另一件更大的事:

JavaScript 模块化的历史,不是一个标准如何自然成熟的历史,而是一整套代码组织秩序,如何在浏览器限制、服务器现实、社区分裂、工具链补位和标准姗姗来迟之间,被一点点逼出来的历史。


这套系列讲什么

我想讲的,不只是“CommonJSAMDESM 分别是什么”。

更想讲的是底下那几场反复重演的冲突:

  • 为什么 JavaScript 明明早就需要模块,却长期没有语言级答案
  • 为什么 CommonJS 会先在 Node 世界赢下秩序,却没法直接统治浏览器
  • 为什么浏览器现实会逼出 AMD / CMD 这种和服务器世界并不相同的路线
  • bundler 为什么会在很长时间里比标准更像真正的宪法
  • ES Modules 为什么来得这么晚,而且来了以后还要继续和旧生态反复摩擦

换句话说,这是一套关于 JavaScript 代码组织史、运行时现实冲突、社区临时制度、以及标准最终接管过程 的系列文章。


这套系列真正要追的,不是语法,是组织权

如果你退一步看,会发现模块化之争表面上像语法之争,底下其实一直在争几件更硬的事。

01|到底是谁有资格定义“一个模块”

今天我们看到 import / export,会很自然觉得:

模块不就是把代码拆开、再按需引进来吗?

可历史上最麻烦的地方恰恰在于:

“模块”从来不只是语法块,它还涉及执行时机、依赖解析、暴露边界、文件组织、加载方式。

也就是说,大家争的不只是写法。

争的是:

  • 模块是不是文件
  • 依赖是不是要先声明
  • 导出是值拷贝还是活绑定
  • 加载是同步还是异步
  • 谁来负责解析和拼装这些关系

所以模块化江湖里第一个最核心的问题就是:

到底谁有资格给 JavaScript 世界定义“一个模块”的合法形态。

02|浏览器现实,还是服务器现实

CommonJS 为什么会在 Node 世界看起来那么自然?

因为它贴着服务器和本地文件系统的现实长出来。

require() 这套直觉,在那样的环境里是顺的。

可一到浏览器,问题就变了。

浏览器不天然站在本地文件系统那边。

它要面对的是:

  • 网络加载
  • 依赖顺序
  • 并发请求
  • 首屏性能
  • 跨域和调试现实

于是同样都叫“模块”,浏览器和服务器其实很长时间里根本不在同一个现实里生活。

所以这套系列会反复追问:

JavaScript 模块化,为什么长期不是“统一技术方案”之争,而是“不同运行时现实谁说了算”之争?

03|社区先活下来,还是标准先定下来

JavaScript 的很多历史都有一个老问题:

标准往往来得不够快。

模块化更是如此。

需求早就存在。

代码规模早就上来了。

工程组织早就需要更稳的秩序。

可标准层长期给不出完整答案。

这时候社区不会停着等。

它一定会先自己发明办法。

于是:

  • Node 社区先有 CommonJS
  • 浏览器社区先有 AMD
  • 各地实践里再长出 CMD
  • 更大的工程压力又催出 bundler

等标准终于赶到时,现实世界已经不是白纸了。

所以模块化江湖里另一个最重要的问题是:

当标准长期缺位时,社区临时制度一旦先活下来,后来者要怎么接管这个世界?

04|统一秩序,还是兼容旧账

模块化最大的不幸之一是:

它不是从零开始建新城。

它总是在一堆旧房子中间修路。

所以每次看似“终于统一”的时刻,后面都拖着长长的兼容账:

  • 旧包怎么处理
  • 老代码怎么迁
  • 双格式怎么共存
  • bundler 和原生模块怎么对齐
  • Node 和浏览器的解释权怎么继续磨

这也是为什么今天你明明已经活在 ESM 时代,却还会不断撞见 CJS

因为模块制度一旦进入生态深处,就不再只是新语法能不能发布的问题。

它会变成:

整个包生态、构建链、运行时约定和开发者习惯,到底愿不愿意一起迁。

05|语言标准,还是工具链现实

模块化江湖还有一个特别典型、也特别 JavaScript 的问题:

很多时候,真正定义现实的不是标准文本,而是工具链。

标准层说的是应然。

可开发者每天真的在用什么,经常由这些东西决定:

  • bundler
  • transpiler
  • package manager
  • runtime loader

也就是说,这套系列里还会一直盯着一个问题:

JavaScript 的模块制度,到底是在规范里被定义的,还是先在工程工具里被做成既成事实的?


为什么模块化这条线特别值得单独拆出来

因为它几乎是现代前端很多“重”的总入口。

你今天觉得前端复杂,往往不是因为 import 这个词本身复杂。

而是因为它背后连着一整串制度后果:

  • 包如何发布
  • 代码如何拆分
  • 依赖如何解析
  • 构建如何发生
  • 浏览器如何加载
  • Node 如何解释
  • 生态如何兼容新旧两套格式

很多人以为模块化只是“组织代码更优雅”。

可历史真相更像:

模块化是现代 JavaScript 世界里最早、也最深地把语言标准、运行时现实、包生态和工程工具链绑在一起的一场制度战争。

如果这一层不拆清楚,后面的:

  • bundler
  • Babel
  • 工程化
  • 包分发
  • Node / 浏览器双端兼容

你都会只看到表层现象,看不到它们为什么会长成今天这样。


这套系列准备怎么写

目前我更倾向于按五篇主线来写:

  1. 没有模块的时代,JavaScript 是怎么靠全局变量硬撑的
    先讲为什么这个需求会出现,以及“脚本标签时代”的根本局限。

  2. CommonJS 为什么先在 Node 世界把秩序立住
    require / exports 背后的运行时现实,以及它为什么天然偏向服务器侧。

  3. AMD / CMD 为什么会在浏览器世界各自长出来
    讲异步加载、浏览器限制、RequireJSSeaJS 和不同社区的选择。

  4. bundler 为什么成了临时宪法
    讲当格式分裂已经成现实,为什么 Webpack 这类工具会成为真正负责收拾残局的人。

  5. ES Modules 为什么赢了,却没有一夜之间结束战争
    ESM 的标准化胜利,以及它为什么还要和 CommonJS、工具链和运行时长期磨合。

也就是说,这套系列想讲的,不只是“谁赢了”。

更想讲:

为什么 JavaScript 模块化最后没有靠某一方彻底碾压收场,而是靠标准、运行时和工具链一起把一场长期内战慢慢熬成了今天的秩序。


先记住这句

如果你现在只想先记住一句话,那就记这句:

JavaScript 模块化的历史,不是 import 语法的诞生史,而是一场围绕“代码到底该怎样被组织、加载、分发和解释”的长期制度战争。

而且这场战争最特别的地方还在于:

它没有一个简单干净的胜利时刻。

CommonJS 没有彻底死。

AMD 没有白来。

bundler 也没有因为 ESM 出现就立刻退场。

这就是 JavaScript 式胜利。

不是一纸标准宣布天下太平。

而是:

旧世界慢慢退,新世界慢慢接,工具链在中间长期做翻译,最后大家才勉强活进同一套现实。


先记住:模块化之争从来不只是语法之争

所以这套 《模块化江湖》 真正想讲的,不是“模块化让代码更清爽”。

更想讲的是:

为什么 JavaScript 这样一门最初连模块制度都没有的语言,最后会把“模块怎么定义”这件事,打成一场牵动浏览器、Node、标准委员会、社区工具和整个包生态的长期内战。

如果把 JavaScript 江湖 写的是这门语言如何一路被推成平台语言,

模块化江湖 要写的,就是:

当它真的开始承受平台级复杂度之后,大家第一次为“怎么组织这门语言本身”狠狠干起来的那场仗。