模块化改造需明确变量归属、封装为可控实例、设计接口通信、支持生命周期管理。按功能域划分模块边界,用单例类封装配置,通过接口解耦模块依赖,并提供init/destroy方法适配容器启停。

旧项目里散落的全局变量,是模块化改造的第一道坎。容器化友好不是指把代码塞进 Docker 镜像,而是让代码结构天然适配容器部署——即:启动快、依赖明、边界清、可替换。关键不在于重写,而在于用模块化手段给老变量“安家”,让它们从“到处乱跑”变成“各守一域”。
明确变量归属,按功能域划出模块边界
先别急着改代码,打开项目全局搜索所有 var、let、const 声明(尤其带 global、config、settings、cache 等关键词的),逐个标注用途:
- 是数据库连接参数?→ 归入
data-source模块 - 是 API 地址或密钥?→ 归入
env-config模块 - 是业务开关(如是否启用优惠券)?→ 归入
feature-flag模块 - 是运行时缓存对象(如用户会话 Map)?→ 归入
runtime-store模块
每个模块对应一个独立目录(如 src/modules/env-config/),不拼凑、不跨域。边界一旦画定,后续所有访问必须走该模块暴露的接口。
封装变量为模块实例,杜绝直接引用
把变量从“裸值”升级为“可控实例”。例如原有一组配置:
// legacy/config.js export const API_BASE_URL = 'https://api.example.com'; export const TIMEOUT_MS = 5000; export const IS_DEV = process.env.NODE_ENV === 'development';
改造后应变为:
// src/modules/env-config/index.ts
class EnvConfig {
private static instance: EnvConfig;
private readonly apiBaseUrl: string;
private readonly timeoutMs: number;
private readonly isDev: boolean;
private constructor() {
this.apiBaseUrl = process.env.API_BASE_URL || 'https://api.example.com';
this.timeoutMs = parseInt(process.env.TIMEOUT_MS || '5000', 10);
this.isDev = process.env.NODE_ENV === 'development';
}
static getInstance(): EnvConfig {
if (!EnvConfig.instance) {
EnvConfig.instance = new EnvConfig();
}
return EnvConfig.instance;
}
getApiBaseUrl(): string { return this.apiBaseUrl; }
getTimeoutMs(): number { return this.timeoutMs; }
isDevelopment(): boolean { return this.isDev; }
}
export default EnvConfig.getInstance();
其他模块不再 import { API_BASE_URL } from '../legacy/config',而是:
import EnvConfig from '@/modules/env-config'; console.log(EnvConfig.getApiBaseUrl());
这样既隔离了原始值,又为未来注入 mock 实例、热更新配置留出空间,也天然支持容器环境变量注入。
设计轻量通信契约,避免模块间隐式耦合
当多个模块需共享某类变量(如当前登录用户信息),禁止互相 import 对方的配置文件。应抽象出最小接口层:
- 定义一个
auth-context.ts接口文件,只声明getUser()、setUser()、clearUser() -
auth-module实现该接口,并管理内部状态 - 其他模块(如订单、通知)只依赖
auth-context类型,不感知实现细节
这种“面向接口编程”的方式,让模块在容器中可被单独替换(比如用 JWT 实现换为 OAuth2 实现),不影响调用方启动与运行。
适配容器生命周期,模块需支持显式初始化与销毁
容器常通过信号(如 SIGTERM)优雅停机。模块若持有定时器、连接池、监听器等资源,必须提供 init() 和 destroy() 方法:
// src/modules/runtime-store/index.ts
export class RuntimeStore {
private cache = new Map<string, any>();
private intervalId: NodeJS.Timeout | null = null;
init() {
// 启动健康检查轮询
this.intervalId = setInterval(() => {
// ...
}, 30_000);
}
destroy() {
if (this.intervalId) clearInterval(this.intervalId);
this.cache.clear();
}
}
export const runtimeStore = new RuntimeStore();
主入口文件(如 main.ts)统一调用所有模块的 init(),并在收到退出信号时反向调用 destroy()。这确保容器缩容或滚动更新时资源不泄漏。

















