精准暴露+渐进封装是改造legacy代码的核心:识别真正被消费的变量(如api_base、isprod)通过exports最小化导出;代理全局变量(如exports.config=config)兼容旧引用;优先拆分高复用低耦合子集(如formatdate);副作用逻辑封装为init函数并按环境自动/手动调用。
直接用 exports 指令改造 legacy 代码中的变量包,核心不是“全量重写”,而是“精准暴露 + 渐进封装”。关键在于识别哪些变量真正需要被外部消费,再通过 commonjs 的 module.exports(或简写 exports)有控制地导出,同时保留原有执行逻辑不变——这样老代码照常运行,新模块可按需引入。
明确导出边界:只暴露“契约接口”,不暴露实现细节
Legacy 代码中常见一个文件定义一堆全局变量(如 CONFIG、utils、API_BASE),直接全部挂到 exports 上等于把包袱原样搬进模块系统。正确做法是:
- 梳理调用方实际依赖了哪些值(例如只有
API_BASE和isProd被其他脚本读取) - 在文件末尾显式导出最小必要集合:
exports.API_BASE = API_BASE;<br>exports.isProd = isProd;
- 删掉或注释掉原用于挂载全局的代码(如
window.API_BASE = ...),避免污染和歧义
兼容旧引用:用 exports 代理全局变量,不改调用方代码
若已有大量 CONFIG.timeout 这类写法,又不能立刻修改所有调用点,可用 exports 做一层轻量代理:
- 保持原变量声明不变(
const CONFIG = { timeout: 5000, retries: 3 };) - 导出时包装为属性访问:
exports.CONFIG = CONFIG; - 后续在新模块中
const { CONFIG } = require('./legacy-config');,语义完全一致 - 未来可逐步将
CONFIG内部重构为函数或 class,导出不变,调用方无感
拆分优先级:从“高复用低耦合”模块开始导出
不要一上来就给整个 utils.js 加 exports。先挑出其中最稳定、被多处引用、且不依赖其他 Legacy 变量的子集,例如:
- 日期格式化函数
formatDate、数字千分位toThousand - 单独提取成
legacy-utils-core.js,只保留这两个函数和必要辅助常量 - 用
exports.formatDate = formatDate;导出,其他未导出函数自动私有化 - 原
utils.js中保留其余逻辑,继续供老代码使用,新项目优先 require 新拆出的模块
处理副作用与初始化逻辑
Legacy 文件常含立即执行代码(如初始化配置、监听事件)。这些不能简单删掉,但也不该随 require 自动触发多次:
- 将副作用逻辑封装进函数:
function init() { /* 原初始化代码 */ } - 在
exports.init = init;同时,加一句if (typeof window !== 'undefined') init();—— 保证浏览器环境首次加载仍生效 - 服务端或测试环境可按需手动调用
require('./legacy').init(),控制权交还开发者










