Node.js 模块加载的路径解析规则是怎样的?

require 路径解析

require('fs')           // 内置模块
require('express')      // node_modules 中的包
require('./lib')        // 相对路径
require('/abs/path')    // 绝对路径

1. 内置模块

require('fs')       // 直接返回内置模块
require('path')     // 最高优先级,不查找 node_modules

2. 相对/绝对路径

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/express

4. 目录加载

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 更强大:

条件导出

{
  "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

总结

  1. 内置模块最高优先级
  2. 相对/绝对路径直接解析
  3. 包名从 node_modules 向上查找
  4. 文件扩展名自动补全:.js → .json → .node
  5. 目录默认加载 index.js 或 package.json 的 main
  6. exports 字段提供更精细的控制

同分类其他题目