老旧javascript项目模块化需分四阶段推进:第一阶段隔离全局污染,用iife和统一命名空间止血;第二阶段抽取高价值模块并替换为esm;第三阶段接入构建工具实现模块化加载;第四阶段建立模块治理规范。

老旧 JavaScript 项目模块化不能一蹴而就,关键在于分阶段、低风险、可验证地推进。核心思路是:先“止血”,再“拆解”,最后“规范”。不追求一步到位,而是让每次改动都可测试、可回滚、不影响线上功能。
第一阶段:隔离全局污染,建立模块边界
目标不是立刻改 import/export,而是终结变量随意挂 window、函数满天飞的状态。这是后续所有重构的前提。
- 用 IIFE 或立即执行函数包裹每个老 JS 文件,切断隐式全局泄露:
(function() { var $ = window.jQuery; function initCart() { ... } })(); - 统一收敛全局命名空间,例如只允许一个 App 对象暴露在 window 上:
window.App = window.App || {};,所有业务逻辑挂载到App.cart、App.user下 - 扫描项目,把重复定义的工具函数(如
formatDate、deepClone)集中到一个 legacy-utils.js,其他地方删掉副本,只调用这一个入口
第二阶段:识别高价值模块,渐进抽取与替换
不按文件数量平均拆,而是优先处理“改一次牵动多处”“逻辑密集且稳定”的模块,比如价格计算、表单校验、数据格式化。
- 用 Lodash 替换手写工具函数:把散落在各处的
removeDuplicate、groupBy、debounce全部替换成_.uniq、_.groupBy、_.debounce—— 这类替换几乎零风险,还能自动解决 null/undefined 边界问题 - 为高频逻辑新建独立模块(ESM 格式),例如
priceUtils.js,导出roundToTwo、formatCurrency等函数;老代码里用import { roundToTwo } from './priceUtils.js'替代内联计算 - 对已有命名空间对象(如
App.cart)逐步封装成模块:先保留App.cart.init()调用,内部实现改为从cartService.js导入函数,实现“接口不变、实现升级”
第三阶段:统一依赖声明与构建接入
当模块达到一定规模(例如 5–8 个核心模块),引入轻量构建链路,让模块真正“活起来”。
- 用原生 ESM 支持度检查(
!window.importMeta)做降级:现代浏览器走<script type="module"></script>,旧版仍加载打包后的 legacy-bundle.js - 配置简易打包脚本(如 esbuild):将所有
.js模块合并为一个app.mjs,启用--tree-shaking自动剔除未使用的导出 - 在 HTML 中统一管理入口:
<script type="module" src="./app.mjs"></script>
不再手动维护 20+ 个<script></script>标签顺序
第四阶段:建立模块治理习惯
技术落地后,机制才能持续。避免回到“谁改得快谁上”的混乱状态。
- 新增代码必须模块化:PR 检查项加入“禁止直接修改 window”“禁止新增全局函数”
- 模块命名约定:工具类用
*Utils.js,服务类用*Service.js,领域逻辑用*Domain.js,避免common.js、helper.js这类模糊命名 - 每个模块加简短 JSDoc 注释说明职责、输入输出、是否纯函数,新人打开文件就能知道“它该干什么、不该干什么”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











