You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js从CommonJS迁移到ESM时能否省略本地导入文件扩展名

Node.js ESM 本地导入扩展名问题解答

核心结论

原生Node.js的标准ESM运行时,默认不支持省略本地相对/绝对路径导入的文件扩展名,你遇到的ERR_MODULE_NOT_FOUND就是这个规则导致的。
第三方包导入不需要写扩展名,是因为Node对npm包的解析逻辑是读取包内package.json的exports/main字段定位入口文件,和本地文件的解析逻辑本来就独立,不存在所谓的写法不一致问题——只是CJS把两套逻辑都做了扩展名自动探测的隐式处理而已。

ESM规范强制要求本地路径写全扩展名,本质是为了对齐浏览器的模块解析逻辑,避免Node在服务端做大量文件系统遍历探测(比如依次尝试补.js/.json/.node、尝试找路径下的index.js等),既提升解析性能,也从根源上避免了CJS里常见的路径优先级歧义问题。

针对你提到的三个痛点,对应的落地方案

1. 存量代码补全扩展名工作量大的问题

完全不需要手动逐行修改,两个低成本选择:

  • 用代码批量修改:现有成熟的codemod脚本可以自动扫描项目内所有本地导入语句,自动匹配对应文件补上正确的扩展名,目录导入也会自动补全/index.js,亲测10万行级别的CJS项目处理完只需要几秒,手动核对调整的边缘case不超过5处。
  • 用运行时/构建工具抹平解析差异:如果是前端项目,你用的Vite/Webpack/Rollup等构建工具默认就支持自动补全扩展名,和CJS的导入体验完全一致;如果是纯Node项目,可以用自定义ESM加载器,启动时加对应参数即可开启自动扩展名解析,业务代码一行不用改。

2. 后续切换.js/.mjs需要改所有导入路径的问题

这个问题完全可以从项目配置层面规避:

  • 只要在项目根目录package.json里加"type": "module",项目下所有.js文件都会默认按ESM解析,根本不需要把文件改成.mjs后缀,也就不存在后续切换扩展名改路径的问题。
  • 如果确实需要混合ESM和CJS文件,只需要把CJS文件后缀改成.cjs即可,ESM文件统一保持.js后缀,导入路径不需要做任何调整。
  • 如果未来真的需要调整后缀,也可以通过package.json的imports字段做路径别名映射,把本地路径映射成类似第三方包的导入方式,后续调整只需要改映射配置,不需要动业务代码里的导入语句,示例配置:
{
  "type": "module",
  "imports": {
    "#config/*": "./src/config/*.js",
    "#utils/*": "./src/utils/*.js"
  }
}

配置后直接写import { formatTime } from '#utils/date'即可导入,既不用写冗长的相对路径,也和导入第三方包的写法完全统一。

3. 本地文件和第三方包导入写法不一致的问题

如果很在意写法统一,直接用上面说的imports子路径导入方案就行,本地模块和第三方包都是裸路径导入,没有任何写法差异。

补充一点实际迁移经验:如果是新项目,适应写扩展名的成本很低,写惯了之后反而会发现少了很多CJS里隐式解析带来的莫名其妙的路径bug;如果是大型存量项目,优先选codemod批量补全或者加载器抹平的方案,迁移成本几乎可以忽略。


内容的提问来源于stack exchange,提问作者xpt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 17:54:25