接手老项目
刚入职的时候,老板让我接手一个老项目。这个项目用 jQuery 写的,已经运行了 5 年,代码有 2 万多行。
打开代码一看,我傻了。全局变量到处都是,函数名都是 doSomething、handleClick 这种,根本看不出是干什么的。注释都是中文,还都是"这里改一下"这种。
代码的问题
这个项目的问题太多了:
- 全局变量到处都是,不知道哪个函数改了哪个变量
- 事件绑定混乱,一个按钮绑了 3 个 click 事件
- DOM 操作直接写,没有封装,重复代码很多
- 没有模块化,所有代码都在一个文件里
- 没有构建工具,直接写 HTML、CSS、JS
最要命的是,这个项目还在用,不能停。每次改功能,都要小心翼翼,生怕改坏了。
决定重构
用了一个月,我实在受不了了。跟老板说,这个项目得重构,不然以后维护成本太高。
老板问要多久,我说至少半年。老板说行,但功能不能停,要边重构边维护。
迁移策略
我们选了渐进式迁移,不是一次性重写:
- 新功能用 React 写
- 老功能逐步迁移,一个模块一个模块来
- 两个系统共存,用 iframe 或者微前端方案
这样风险小,不会一次性改坏所有功能。
第一个模块:用户登录
选了最简单的模块开始:用户登录。这个模块逻辑简单,改起来风险小。
但改起来才发现,没那么简单。jQuery 的登录逻辑和 React 的登录逻辑不一样:
- jQuery 直接操作 DOM,React 用状态管理
- jQuery 的事件处理是命令式的,React 是声明式的
- jQuery 的 AJAX 用
$.ajax,React 用fetch或axios
花了 2 周,才把登录模块迁移完。测试了好几遍,确保没问题。
第二个模块:数据列表
登录模块迁移完,开始迁移数据列表。这个模块复杂一些,有分页、搜索、排序。
jQuery 的代码是这样的:
$('#search-btn').click(function() { var keyword = $('#keyword').val(); $.ajax({ url: '/api/search', data: { keyword: keyword }, success: function(data) { $('#result-list').html(''); data.forEach(function(item) { $('#result-list').append('<div>' + item.name + '</div>'); }); } }); });React 的代码是这样的:
const [keyword, setKeyword] = useState(''); const [results, setResults] = useState([]); const handleSearch = async () => { const response = await fetch(`/api/search?keyword=${keyword}`); const data = await response.json(); setResults(data); }; return ( <div> <input value={keyword} onChange={e => setKeyword(e.target.value)} /> <button onClick={handleSearch}>搜索</button> {results.map(item => <div key={item.id}>{item.name}</div>)} </div> );看起来 React 的代码更清晰,但迁移起来很麻烦。要理解 jQuery 的逻辑,再转换成 React 的逻辑。
遇到的坑
坑一:全局状态
jQuery 的代码用全局变量存状态,比如 var currentUser = null;。React 要用 Context 或者状态管理库。
我们用了 Context,但发现 Context 更新会导致所有组件重渲染。后来用了 Zustand,才解决了问题。
坑二:事件绑定
jQuery 的事件绑定是动态的,可以随时绑定和解绑。React 的事件绑定是静态的,在组件渲染时确定。
有些复杂的交互,jQuery 用事件委托,React 要重新设计组件结构。
坑三:DOM 操作
jQuery 直接操作 DOM,比如 $('#element').show()。React 用状态控制显示隐藏,比如 {isVisible && <div>...</div>}。
有些复杂的 DOM 操作,jQuery 很简单,React 要写很多代码。
迁移进度
迁移了半年,进度如下:
- 用户登录:完成
- 数据列表:完成
- 表单提交:完成
- 文件上传:完成
- 图表展示:进行中
- 复杂交互:还没开始
大概完成了 60%,剩下的都是复杂的模块,迁移起来更麻烦。
迁移的收获
虽然迁移很痛苦,但收获也很多:
- 理解了老代码的逻辑,虽然很乱,但至少知道了
- 学会了渐进式迁移,不是所有重构都要一次性完成
- 提升了 React 技能,写了很多 React 代码
- 改善了代码质量,新代码比老代码清晰很多
如果重新开始
如果重新迁移一个老项目,我会:
- 先梳理代码结构,理解业务逻辑
- 从最简单的模块开始,逐步迁移
- 做好测试,确保迁移后功能正常
- 统一代码规范,新代码要清晰
总结
重构老代码是个痛苦的过程,但也是必要的。老代码不重构,维护成本会越来越高。
如果你们项目也有老代码要重构,建议用渐进式迁移,不要一次性重写。风险小,效果好。
另外,重构要有耐心。半年时间看起来很长,但比起以后一直维护老代码,这个时间还是值得的。
评论区