Node.js模块查找遵循内置模块→路径模块→裸模块的优先级顺序,裸模块按从导入位置向上逐级搜索node_modules,就近原则生效;入口解析依次检查exports、main字段及index文件。

Node.js 查找模块时,并不是简单地在当前目录扫一遍 node_modules,而是有一套明确、可预测的路径解析规则。理解这套逻辑,能帮你快速定位“为什么模块找不到”、“为什么引入了错误版本”、“如何控制依赖优先级”等问题。
模块查找分两类:内置模块、文件路径模块、第三方模块
Node.js 先按顺序判断模块类型,再走不同路径逻辑:
-
内置模块(如
fs、path、events):直接返回,不查文件系统 -
相对/绝对路径模块(如
./utils、../config.js、/home/user/app/index.js):按字面路径加载,带扩展名则精确匹配;不带则依次尝试.js、.json、.node -
裸模块名(如
lodash、express):进入核心的node_modules查找逻辑
裸模块的 node\_modules 查找路径(从 import 位置向上逐级)
假设你在 /a/b/c/d.js 中写了 require('foo'),Node.js 会按以下顺序搜索:
/a/b/c/node_modules/foo/a/b/node_modules/foo/a/node_modules/foo/node_modules/foo
找到第一个存在的 foo 目录即停止。这意味着:嵌套越深的 node_modules 优先级越高,“就近原则”生效。这也是 pnpm 的硬链接结构和 yarn pnp 能工作的基础——只要路径符合该规则,Node 就能加载。
立即学习“Java免费学习笔记(深入)”;
使用一条命令部署ProbeChain Rydberg测试网代理节点。自动注册为Agent(NodeType=1),免gas,支持macOS/Linux/Windows。触发词:/r
package.json 的 "main" 和入口解析细节
找到 node_modules/foo 后,Node 并不直接执行任意文件,而是按顺序读取入口:
- 先看
package.json中的"main"字段(默认index.js) - 若无
main,且有"exports"字段,则按exports规则解析(支持条件导出、子路径导入等) - 若两者都无,尝试加载
index.js→index.json→index.node
注意:"exports" 是 Node.js 12.20+ 引入的现代机制,一旦声明,就会屏蔽 main 和直接文件访问(比如 require('foo/lib/utils') 会报错,除非 exports 显式开放)。
全局安装模块不参与常规查找
npm install -g 安装的模块(如 nodemon)不会被 require() 自动加载。它们通常只注册为 shell 命令,或放在全局 node_modules 目录(可通过 npm root -g 查看)。想在代码中用,必须手动配置 NODE_PATH 或使用 require.resolve + require 动态加载(不推荐,破坏可移植性)。
不复杂但容易忽略:模块解析是同步、确定性、自底向上的过程,没有“扫描整个硬盘”或“智能猜测”。看清 require 所在文件的位置,再对照路径规则推演,90% 的模块解析问题都能定位清楚。

















