NextJS 15中多路由无法共享单例Vault实例问题
问题分析与解决方案
问题根源
你的单例在Next.js 15的两个路由中被重复创建,核心原因有两个:
- 开发模式的模块隔离与热重载:Next.js App Router在开发时,每个路由模块会被单独编译和热重载,模块级的
instance变量会在路由重载时重置,导致重新创建实例。 - 生产环境的进程模型:如果部署在无服务器函数(如Vercel、Netlify)或Node.js集群模式下,每个进程拥有独立的内存空间,模块级单例仅在单个进程内有效,跨进程无法共享。
解决方案
方案1:开发测试用——基于全局对象的单例
利用Node.js的globalThis存储实例,避免模块重复加载导致的变量重置,适合开发单进程场景:
import { Vault } from './Vault'; type VaultType = Vault; export function getOrCreateVaultInstance(keys?: Map<string, string>): Vault { // 用globalThis存储进程级实例 if (!globalThis.vaultInstance) { console.info("✨ Creating a new vault instance ✨"); globalThis.vaultInstance = new Vault(keys); } return globalThis.vaultInstance as Vault; } // 扩展TypeScript全局类型,避免类型报错 declare global { var vaultInstance: VaultType | undefined; }
注意:开发模式下如果重启整个服务器进程,实例仍会重置,但单个路由热重载不会影响;生产单进程部署可正常共享。
方案2:生产环境推荐——外部缓存服务
内存单例无法跨进程/无服务器实例共享,生产环境必须用外部缓存存储验证码,比如Redis:
// 先安装依赖:npm install redis import { createClient } from 'redis'; // 初始化Redis客户端(从环境变量读取配置) const redisClient = createClient({ url: process.env.REDIS_URL }); redisClient.connect().catch(console.error); // 存储验证码(设置5分钟过期) export async function saveVerificationCode(email: string, code: string) { await redisClient.setEx(email, 300, code); } // 验证验证码 export async function verifyVerificationCode(email: string, code: string) { const storedCode = await redisClient.get(email); return storedCode === code; }
优势:不受进程限制,数据持久化(可选),更符合生产环境的可靠性要求,同时避免服务器重启导致的数据丢失。
总结
- 仅开发测试时,可使用
globalThis实现单例共享; - 生产环境必须依赖外部缓存服务,这是Next.js部署模型下的唯一可靠方案。
内容的提问来源于stack exchange,提问作者Reza Attar
相关产品推荐
相关产品推荐

