node.js 的 require 缓存机制天然实现模块状态持久化:模块首次加载后,其 module.exports 被缓存复用,所有 require 调用共享同一导出对象及其中的变量、实例与状态;require.cache 是该状态的物理存储,支持读取与手动清理;循环依赖中亦能安全访问“半初始化”状态;此机制是加载模型的自然结果,适用于单例服务等内存级状态管理场景。

Node.js 的 require 缓存机制天然支持模块状态的持久化——不是靠手动保存,而是靠模块加载时的单次执行与缓存复用。只要模块被 require 过一次,它的 module.exports 就被固定在 require.cache 中,后续所有引用都共享同一份导出对象,其中包含的所有变量、函数、实例或内部状态,都会持续存在并可被修改、读取。
模块导出即状态载体
模块文件中的顶层变量(非 const / let 声明的可变绑定)一旦挂载到 module.exports 或其子属性上,就成为跨多次 require 调用可访问的状态。
- 例如,一个计数器模块:
let count = 0;<br>module.exports = {<br> inc() { count++ },<br> get() { return count }<br>};
无论你在不同文件中调用多少次require('./counter'),拿到的都是同一个对象,inc()修改的是同一份count。 - 导出类的实例、连接池、配置对象等,也都遵循此规则——它们在首次加载时创建,之后始终复用。
require.cache 是状态存在的物理位置
require.cache 是一个普通 JavaScript 对象,键为模块绝对路径,值为 Module 实例。其中 module.exports 属性直接持有模块对外暴露的全部状态。
- 你可以随时检查:
console.log(require.cache[require.resolve('./config.js')].exports) - 也可以手动清理缓存强制重载(仅限开发调试):
delete require.cache[require.resolve('./data.js')],下次require就会重新执行模块代码、初始化新状态。 - 注意:
exports只是module.exports的初始引用别名;若你给exports重新赋值(如exports = {}),会断开与module.exports的联系,导致缓存中实际导出的是旧的module.exports,容易引发状态不一致问题。
循环依赖中的状态可见性
当两个模块相互 require 时,缓存机制让它们能安全地拿到对方“正在初始化中”的导出对象——也就是已创建但尚未执行完的 module.exports。
- 这意味着:A 模块在加载过程中就能访问 B 模块已挂载的属性(哪怕 B 还没执行完),B 同理。这种“半初始化”状态也是持久化的组成部分。
- 它避免了死锁,也使得模块间共享状态(如事件总线、全局配置)在循环依赖场景下依然可用。
这不是“设计出来的持久化”,而是加载模型的自然结果
require 缓存不提供 TTL、序列化或跨进程共享能力,它只保证:同一进程内,相同路径的模块只执行一次,导出对象恒定复用。这恰好构成了轻量级、零配置、内存级的状态持久化基础——适用于单例服务、运行时配置、连接管理、内存缓存容器等场景。











