JavaScript模块化依赖明确规范与环境支持:ESM需type="module"或.mjs后缀,import必须顶层静态声明;CommonJS仅Node原生支持,浏览器需打包器转换;二者机制冲突,混用易出错。

JavaScript 模块化不是靠某一个“开关”或语法糖自动实现的,而是依赖明确的模块格式规范 + 构建工具或运行时环境支持。没有 import / export 就没有 ES 模块,没有 require 和 module.exports 就没有 CommonJS 模块——它们不是可选特性,是强制约定。
ES 模块(ESM)必须用 type="module" 或 .mjs 后缀启动
浏览器中直接写 import 会报错:Uncaught SyntaxError: Cannot use import statement outside a module。原因很简单:默认 HTML 脚本是 classic script,不解析模块语法。
- 在
标签中启用 ESM,必须加type="module"属性 - Node.js 中,要么文件后缀为
.mjs,要么在package.json中声明"type": "module" - 不满足任一条件,
import会直接抛语法错误,且无法降级为 CommonJS - ESM 是静态分析的——
import必须在顶层,不能放在if或函数里;动态导入要用import()函数形式
CommonJS 只在 Node.js 默认生效,浏览器里需要打包器
require() 和 module.exports 是 Node.js 的原生模块系统,但浏览器根本不认识它们。想在浏览器用,必须借助 Webpack、Vite、Rollup 等工具把 CommonJS 转成可执行代码。
- Node.js v12+ 默认支持 ESM,但 CommonJS 仍是默认行为(除非显式开启
type: "module") - CommonJS 是运行时加载:
require()返回值可以是任意类型,支持循环引用(有缓存机制) - 混用 ESM 和 CommonJS 风险高:比如在 ESM 文件中
import一个 CommonJS 模块,得到的是default属性包裹的对象,而非导出值本身 - Vite 在开发时能处理混合导入,但生产构建可能出问题,尤其涉及
__esModule标记和default提取逻辑
import 和 require 的加载时机与作用域完全不同
这不是风格差异,是底层机制冲突。ESM 是编译时静态链接,CommonJS 是执行时动态求值——这意味着它们对变量提升、作用域封闭、副作用触发时机的处理完全相反。
立即学习“Java免费学习笔记(深入)”;
-
import声明不会被提升到块顶,但整个模块作用域会被提前解析(hoisting 是模块级的,不是语句级) -
require()是同步阻塞调用,会立即执行模块脚本并返回结果;而import()是异步 Promise,适合按需加载 - ESM 中的顶层
this是undefined;CommonJS 中是当前模块的exports对象 - 试图在 ESM 中用
require(比如通过module.require)会破坏模块图完整性,Vite/Webpack 通常不支持
真正难的不是写 export,而是理解不同模块系统之间如何桥接、何时该用动态 import()、以及为什么某些库(比如早期的 Lodash)要提供 lodash-es 和 lodash 两个版本——本质是导出格式与消费方式的耦合。没理清这点,哪怕语法全对,也会在 tree-shaking、循环依赖、服务端渲染时掉进坑里。











