前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库Node.js 模块格式判定 package.json type 字段
NoNode.js模块与工具链

Node.js 怎样判断 .js 文件是 CommonJS 还是 ESM?

先找到实际执行的文件与它所属的包作用域,再讨论 import 或 require 为什么报错。

前端进阶之旅 · 一题精讲更新于 2026.09.06
Node.js#模块与工具链#构建工具
先看核心答案读代码示例
理解线索

从外到内定位解析环境

  1. 文件身份确认执行的是源文件、编译产物还是另一份依赖
  2. 显式标记检查扩展名与最近的 package.json type
  3. 环境规则核对 Node 版本、启动参数及自定义加载工具

模块格式判定与包入口解析是不同问题:exports 可以选择一个入口文件,选中后仍需要按该文件的格式规则执行。

核心回答

先记住这个答案

对常规 JavaScript 文件,.mjs 明确表示 ESM,.cjs 明确表示 CommonJS;.js 主要看最近父级 package.json 的 type,module 与 commonjs 分别指定两种格式。缺少明确标记的歧义输入在支持语法检测的 Node 中还可能按 ESM 专用语法识别,Node 22.7.0 默认启用了这项检测。动态 import() 在两种格式里都合法,不会单独强制文件变成 ESM。工程上应显式写 type 或扩展名,并检查最终产物及实际启动方式,避免依赖版本相关的推断。

  • .mjs 和 .cjs 比包级默认更直接
  • 嵌套 package.json 会改变附近 .js 文件的作用域
  • 动态 import 表达式本身不是 ESM 专属语法

扩展名与包作用域共同决定普通文件

假设仓库根目录声明 type 为 module,根下的 app.js 会按 ESM 解释;其中一个子目录可以放置自己的 package.json 并声明 commonjs,使该作用域里的 .js 使用 CommonJS。外层配置不会越过这个新的包边界继续覆盖它。

下面把同名类型概念落实到三个文件。执行 node main.js 时,入口使用静态 import,工具文件使用 module.exports;两者通过互操作协作,不需要把整个仓库强制改成同一种格式。

显式包作用域示例Text
// file: package.json
{ "type": "module" }

// file: tools/package.json
{ "type": "commonjs" }

// file: tools/value.js
module.exports = { value: 7 };

// file: main.js
import data from './tools/value.js';
console.log(data.value);

这些文件按标记分别保存后运行 node main.js,输出 7。若把工具改成 .cjs,扩展名就能表达其格式;若改成 .mjs,则不能继续把 module.exports 当作 ESM 的导出接口。

歧义输入的语法检测不能当作项目配置

现代 Node 可以检查没有明确格式标记的输入,识别静态 import、export、import.meta 等只能按 ESM 成功解释的语法。这个行为随版本演进,不能把“没写 type 永远是 CommonJS”当成跨版本规则。

同时,动态 import() 可以出现在 CommonJS 中,因此看见 import 字样就推断 ESM 也不准确。排查需要读具体语法、包边界与命令参数;显式声明能让工具更一致,也减少运行时猜测和相关提示。

区分 Node、转译器与打包器各自的决定

TypeScript 或打包工具可能把源码 import 转成 require,再输出到另一个目录。源码检查通过后,产物若落进 type 为 module 的 .js 作用域,仍可能因 require 不存在而失败,问题发生在产物与执行环境的组合上。

命令行字符串和标准输入又有 --input-type 等独立控制方式,不应把临时 node -e 的结果直接等同于磁盘文件。复现时记录完整入口、文件扩展名、最近配置与 Node 版本,才能缩小差异。

回答前,多想一步

容易答错的地方

只改 type 不检查产物扩展名
包级切换会影响很多 .js 文件,包括脚本与配置。应检查哪些文件仍输出 CommonJS,必要时保留 .cjs 或独立作用域,并验证实际运行入口,避免一次配置修改造成批量运行时故障。
认为调用 require 就把目标变成 CommonJS
加载器调用方式与目标文件格式不能简单画等号,支持互操作的 Node 可以从 CommonJS 加载符合条件的 ESM。目标解析仍受格式标记影响,异步模块图等限制也不会因此消失。
试着用自己的话回答

面试官还会怎么问?

package.json 的 main 能指定模块格式吗?

main 主要描述入口位置,不能代替 type 或明确扩展名来声明文件格式。即使入口解析到了正确路径,内容和格式不匹配仍会失败,因此要分别验证入口解析和模块求值。

浏览器中的 type module 和包字段一样吗?

二者处在不同执行环境:浏览器通过脚本加载方式识别模块,不会遍历服务器目录读取 package.json。前端构建可能连接这些约定,但不能把 Node 的磁盘包作用域规则直接套到浏览器。

为什么编辑器不报错而 node 执行失败?

编辑器可能依据 TypeScript 配置和类型声明判断源码,而 Node 执行的是生成后的 JavaScript 及其所在目录规则。应检查构建产物和真实启动命令,类型检查通过不代表模块格式组合一定可运行。

从一道题,走向一组知识

把知识连起来

模块与工具链

CommonJS 从哪个 Node.js 版本可以 require ESM,为什么仍可能失败?

继续检查跨格式加载对版本与同步求值的要求。

模块与工具链

CommonJS 循环依赖为什么会读到尚未初始化完成的导出?

确认采用 CommonJS 后再分析缓存与初始化环。

模块与工具链

ESM 导入 CommonJS 时,为什么默认导入可用而命名导入报错?

理解示例中 ESM 默认导入 CommonJS 的返回形状。

参考资料

  • Node.js 22:包、扩展名与语法检测
  • Node.js 22:两种模块加载方式

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 扩展名与包作用域共同决定普通文件
  3. 歧义输入的语法检测不能当作项目配置
  4. 区分 Node、转译器与打包器各自的决定
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑