ES6模块与CommonJS本质不同:ES6为静态编译时加载、绑定不可变,仅支持顶层import;CommonJS为动态运行时加载、可多次require并缓存。二者不可混用,否则报错;互操作需注意default包装差异,工具链不自动修正语义。

ES6 模块(import/export)和 CommonJS(require/module.exports)不是简单“换写法”的关系——它们在加载时机、绑定方式、运行环境和工具链支持上根本不同。直接混用或强行转换会触发 ReferenceError、TypeError: Cannot assign to read only property 'exports' 或打包后模块为空。
ES6 模块的 import 必须在顶层,不能放在 if 或函数里
这是语法限制,不是建议。V8 和所有现代打包器(Webpack、Rollup、Vite)都严格校验这一点。
-
import是静态声明:编译期就确定依赖图,不支持动态路径(如import('./' + name + '.js')除外,但那是import()动态导入,返回 Promise) - 常见错误:在
if (process.env.NODE_ENV === 'dev') { import('./debug.js') }—— 会报Unexpected token 'import' - 替代方案:用
import()动态导入,例如if (debug) { const debugModule = await import('./debug.js'); debugModule.init(); }
CommonJS 的 require 可以随时调用,但无法被原生浏览器执行
Node.js 默认用 CommonJS,浏览器原生只认 ES6 模块(type="module")。两者不是“谁更好”,而是“谁在哪跑得起来”。
-
require是同步运行时求值:每次调用都执行模块代码,可多次require('./a.js'),返回同一缓存对象(require.cache控制) - Node.js 中启用 ES6 模块需满足任一条件:文件扩展名为
.mjs,或package.json中设置"type": "module" - 若在
.cjs文件里写import,或在.mjs里写require,Node 会直接报错:Cannot use import statement outside a module或require is not defined
export default 和 module.exports 表面相似,实际语义不同
别被名字骗了。export default xxx 导出的是一个“默认绑定”,而 module.exports = xxx 是直接替换整个 exports 对象。
立即学习“Java免费学习笔记(深入)”;
- ES6 中
export default function foo() {}等价于export { foo as default },本质是具名导出加别名;它不要求接收方必须用import foo from './x.js',也可以import { default as foo } from './x.js' - CommonJS 中
module.exports = function() {}后,require('./x.js')得到的就是这个函数;但若写成exports.default = function() {},则必须require('./x.js').default才能取到 - 跨格式互操作时(比如在 ESM 中
import * as cjsMod from './x.cjs'),Node 会把 CommonJS 模块包装成命名空间对象,cjsMod.default才对应module.exports的值——这点极易忽略,导致调用时报is not a function
Vite/Webpack 等工具不会自动修正模块类型冲突
它们可以“处理”两种语法,但不会帮你改语义。你写的 export default 就是 ESM,默认按 ESM 规则解析;你写的 module.exports 就是 CommonJS,默认走 CJS 加载逻辑。
- Webpack 中若同时存在
import和require,它会生成兼容代码,但模块间 this 值、__dirname、require.resolve等行为仍按原始模块类型保留 - Vite 在开发服务器中对
.cjs文件默认当作普通脚本加载(不解析require),除非显式配置optimizeDeps.include或使用ssr: true - 最稳妥的做法:项目统一模块系统。新项目优先用
.mjs+import/export;维护老 Node 工具库时,若要支持 ESM 消费者,必须在package.json中加"exports"字段并指定"import"和"require"入口
真正卡住人的从来不是语法怎么写,而是搞不清当前代码是在哪个模块系统下被解析、被缓存、被重命名——尤其是 export default 被当成命名导出、module.exports 被包装进 default 属性这种隐式转换。











