commonjs模块在node.js中默认被require.cache缓存,重载需手动清除缓存并重新require,仅限开发环境;require.cache以绝对路径为键,含exports和loaded属性;可手动或递归清除缓存,但无法还原副作用,生产环境禁用。

CommonJS 模块在 Node.js 中默认被缓存(require.cache),一旦加载,后续 require() 都返回缓存副本,不会重新执行模块代码。因此“重载”并非语言原生支持的功能,而是通过手动清除缓存、重新 require 实现的临时调试手段——**仅适用于开发环境,严禁用于生产**。
理解 require.cache 的结构与行为
require.cache 是一个以模块绝对路径为键(key)、模块对象(Module 实例)为值的普通对象。每个模块对象的 exports 属性保存导出内容,loaded 为 true 表示已加载完成。
- 模块路径是解析后的绝对路径(如
/Users/xxx/project/utils/log.js),不是相对路径或裸名 - 依赖子模块也会被独立缓存,清除父模块缓存不会自动清除其依赖
-
require.resolve()可安全获取模块的绝对路径,避免手拼路径出错
手动清除单个模块缓存
最直接的方式:用 require.resolve() 获取路径,再从 require.cache 中删除对应项,并可选地 delete 其 parent.children 引用(防止旧模块仍被持有)。
function reloadModule(modulePath) {
const absPath = require.resolve(modulePath);
delete require.cache[absPath];
// 清理父引用(可选,有助于 GC)
Object.values(require.cache).forEach(mod => {
mod.children = mod.children.filter(child => child.id !== absPath);
});
return require(absPath); // 重新加载
}
// 使用示例
const logUtils = reloadModule('./utils/log');
递归清除模块及其所有依赖缓存
若模块 A 依赖 B,B 依赖 C,只清 A 的缓存后再次 require(A),B 和 C 仍走缓存。要真正“热重载”,需递归收集并清除整个依赖树:
- 从目标模块开始,遍历
module.children获取直接依赖 - 对每个子模块递归调用相同逻辑,收集全部路径
- 统一清除所有路径对应的缓存项
- 最后重新
require目标模块
注意:循环依赖需加访问标记避免死循环;node_modules 中的包一般不建议清除,可过滤掉 /node_modules/ 路径。
实用限制与替代建议
模块重载有本质局限:
- 无法还原已绑定的闭包变量、定时器、事件监听器等副作用
- 类实例、全局状态、单例对象不会自动更新,需手动重建或重置
- ESM(
import)不支持运行时缓存清除,CommonJS 重载方案对其无效
更健壮的开发方案包括:使用 nodemon 或 ts-node --watch 自动重启进程;将业务逻辑封装为可实例化的类,通过 new 创建新实例替代重载;借助 Vite / Webpack HMR 处理前端模块热更新。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











