模块化是组织代码的思路而非语法特性,解决变量污染、依赖混乱和复用困难;CommonJS 同步执行、适合服务端,ES Modules 静态解析、支持 tree-shaking,二者在环境、语法和加载机制上存在根本差异。

模块化不是一种语法特性,而是组织代码的思路——它解决的是变量污染、依赖混乱和复用困难这三个实际问题。JavaScript 本身没有“模块”这个运行时概念,所有模块方案都是构建时或规范层面的约定。
CommonJS 是 Node.js 默认的模块系统
它用 require() 加载,module.exports 或 exports 导出,同步执行,天然适合服务端。
-
require()每次调用都会缓存结果,重复引入不会重复执行模块代码 -
exports是module.exports的引用,直接赋值exports = {...}会断开连接,必须用module.exports = {...} - 不能在条件语句里动态
require(),否则打包工具(如 Webpack)无法静态分析依赖
ES Modules(ESM)是浏览器和现代 Node 的标准模块语法
它用 import/export,静态编译期解析,支持 tree-shaking,但不支持运行时加载路径变量。
-
import必须写在顶层作用域,不能放在if或函数里(除非用import()动态导入) -
export default只能有一个,命名导出可多个;对应import x from和import { a, b } from - Node 中启用 ESM 需满足任一条件:
.mjs后缀、package.json中设置"type": "module"、或命令行加--experimental-modules(旧版本)
浏览器中直接使用 ESM 要注意路径和 MIME 类型
HTML 中通过 加载,此时路径必须是相对或绝对 URL,不能是纯模块名(如 lodash)。
立即学习“Java免费学习笔记(深入)”;
- 本地文件协议(
file://)下,ESM 会因 CORS 被拒,必须起一个本地服务(如npx serve) - 导入路径末尾不能省略扩展名(
./utils.js可,./utils不可),除非服务器配置了自动映射 - 动态导入
import('./foo.js')返回 Promise,可用于按需加载、条件加载或错误降级
真正难的不是写 export 或 import,而是搞清当前环境(Node 版本、打包工具、是否启用实验特性)到底走哪条模块解析逻辑——同一份代码,在 node -r esm、ts-node、Vite 和 Webpack 下行为可能完全不同。











