Node.js 模块加载的路径解析规则是怎样的?
require 路径解析
require('fs') // 内置模块
require('express') // node_modules 中的包
require('./lib') // 相对路径
require('/abs/path') // 绝对路径1. 内置模块
require('fs') // 直接返回内置模块
require('path') // 最高优先级,不查找 node_modules2. 相对/绝对路径
require('./lib')
// 解析为 /当前目录/lib
// 然后尝试:
// 1. /当前目录/lib(精确文件)
// 2. /当前目录/lib.js
// 3. /当前目录/lib.json
// 4. /当前目录/lib.node
// 5. /当前目录/lib/index.js(目录)
// 6. /当前目录/lib/package.json 的 main 字段3. node_modules 查找
require('express')
// 从当前目录向上逐级查找 node_modules:
// /home/user/project/node_modules/express
// /home/user/node_modules/express
// /home/node_modules/express
// /node_modules/express4. 目录加载
require('./utils')
// 如果 utils 是目录:
// 1. 读取 utils/package.json 的 main 字段
// 2. 如果没有 main,默认 index.js
// 3. 尝试 utils/index.js// package.json
{
"name": "utils",
"main": "./lib/index.js" // require('./utils') 加载此文件
}exports 字段(Node.js 12+)
// package.json
{
"name": "my-pkg",
"exports": {
".": "./dist/index.js", // require('my-pkg')
"./utils": "./dist/utils.js", // require('my-pkg/utils')
"./package.json": "./package.json"
}
}exports 比 main 更强大:
- 可以定义子路径
- 可以区分 CJS/ESM
- 未在 exports 中定义的路径不可访问
条件导出
{
"exports": {
".": {
"import": "./dist/index.mjs", // ESM 入口
"require": "./dist/index.cjs", // CJS 入口
"default": "./dist/index.cjs" // 降级
}
}
}ESM 的 import 解析
ESM 解析规则类似但有不同:
// ESM 必须写扩展名(浏览器中)
import './lib.js' // ✅
import './lib' // ❌(浏览器中报错,Node.js 中可以)
// Node.js 中 ESM 的解析
import './lib.mjs' // 明确 ESM
import 'express' // 从 node_modules 查找解析调试
// 查看模块解析路径
console.log(require.resolve.paths('express'))
// ['/home/user/project/node_modules', '/home/user/node_modules', ...]
// 查看实际加载的文件
console.log(require.resolve('express'))
// '/home/user/project/node_modules/express/index.js'
// require.resolve 的选项
require.resolve('./lib', {
paths: ['/custom/path'] // 自定义查找路径
})NODE_PATH
# 环境变量指定额外查找路径(不推荐)
NODE_PATH=/custom/modules node app.js总结
- 内置模块最高优先级
- 相对/绝对路径直接解析
- 包名从 node_modules 向上查找
- 文件扩展名自动补全:.js → .json → .node
- 目录默认加载 index.js 或 package.json 的 main
- exports 字段提供更精细的控制