因为node.js模块加载器硬性禁止commonjs的require()加载es module文件,这是解析阶段的强制拦截,而非语法或兼容性问题。

为什么 require() 一个 .mjs 文件会报 ERR_REQUIRE_ESM
因为 Node.js 严格禁止用 CommonJS 的 require() 加载 ES Module 文件——这不是兼容性问题,而是模块加载器的硬性限制。哪怕你只在 index.js(CommonJS)里写了一行 require('./utils.mjs'),Node 就会立刻抛出 Error [ERR_REQUIRE_ESM]: require() of ES Module ... not supported。
常见触发场景:
- 第三方包升级后默认发布为 ESM(如
execa@8+、glob@10+),但你的主入口仍是require() - 自己写了
.mjs工具脚本(比如 CLI 脚本),却被require()直接引入 - VSCode 的“运行代码”插件(如 Code Runner)默认用
node xxx.js执行,不区分模块类型
根本原因不是语法写错,而是加载链路断裂:CommonJS 加载器无法解析 ESM 的静态导出声明。
如何让 import 和 require 在同一项目里共存而不炸
不能靠“改后缀”或“加 type: "module"”一刀切——那会让所有 .js 文件变成 ESM,导致旧的 require('./config.js') 全部失效。必须分层控制:
- 明确划分边界:主入口(如
main.js)保持 CommonJS;新功能模块(如cli/下的命令)用.mjs+import - 跨模块调用时,用动态
import()替代require():在 CommonJS 文件中写const mod = await import('./utils.mjs');是合法且被 Node 支持的 - 如果必须同步获取 ESM 模块(比如 CLI 参数解析需在顶层执行),就用
createRequire构造一个 CommonJS 加载器:import { createRequire } from 'module';<br>const require = createRequire(import.meta.url);<br>const config = require('./config.cjs'); // 只能加载 .cjs 或 .js(且 package.json 未设 type: module)
注意:createRequire 创建的 require 仍不能加载 .mjs,它只是帮你绕过顶层 require() 的限制,去加载传统 CJS 模块。
VSCode 里点“运行”就报 Unexpected token 'export' 怎么办
这是最典型的编辑器执行环境错配:你写的 script.js 用了 export default,但 VSCode 默认用 node script.js 启动——而 Node 把它当 CommonJS 解析,自然不认识 export 关键字。
使用一条命令部署ProbeChain Rydberg测试网代理节点。自动注册为Agent(NodeType=1),免gas,支持macOS/Linux/Windows。触发词:/r
有三个互斥解法,选一个即可:
- 在
package.json加"type": "module":之后所有.js都按 ESM 处理,但所有require()调用必须重写为import,且不能混用__dirname等 CJS 变量 - 把文件后缀改成
.mjs:VSCode 运行时自动识别为 ESM,无需改package.json,但要注意import语句里的路径也得对应(import x from './util.mjs') - 在 VSCode 的
launch.json里显式指定执行命令:"runtimeArgs": ["--input-type=module"],<br>"args": ["--eval", "import('./script.js')"]这样绕过文件扩展名判断,强制以 ESM 方式执行
推荐优先用 .mjs 后缀——改动最小、意图最清晰,也不会影响项目里已有的 CJS 配置文件。
ESBuild 或其他打包工具里 import * as 引入 CommonJS 包失败
比如 import * as execa from 'execa' 在 ESBuild 中生成的代码里,execa 变成一个命名空间对象,而实际 execa 是个函数,调用时就会报 execa is not a function。
这是因为 ESBuild 默认把 CommonJS 模块转成命名空间对象,而不是提升默认导出。解决方式取决于你用的工具链:
- ESBuild:加
--platform=node --main-fields=module,main,并确保目标包的package.json里"module"字段指向正确的 ESM 入口;若包没提供module,就用inject选项注入兼容层 - Vite / Webpack:配置
resolve.alias或optimizeDeps.exclude把问题包排除在预构建外,让它走原始 CommonJS 路径 - 纯 Node 运行时:放弃
import *,改用import execa from 'execa'(需包支持默认导出)或const execa = (await import('execa')).default
本质是工具对模块规范的解释差异,没有银弹——必须看具体包的导出形态和你使用的构建器配置。
真正麻烦的从来不是“怎么写”,而是“谁在什么时候、用什么方式加载了哪个文件”。模块系统不是语法糖,它是运行时契约。一旦混合,就得全程盯住加载器行为,而不是只盯着 import 和 require 的写法。










