怎么就接下了这个烫手山芋
我跟你们说,这个项目,就是个历史遗留问题,一个烂摊子。那个老系统,名字叫啥不重要,关键是它的核心部分,用的是一个贼老的TS版本。老到什么程度?估计得是四五年前的东西了。一开始我觉得,跑得好好的,就别动了呗。老话说得不坏别修。但是,它开始自己找事了。
去年夏天,我们打算上一个新的功能,需要用几个现在流行的库。新库一装进去,好家伙,直接报错,说我们这边的TS太老了,兼容不了现代的类型定义。这就像你找了个新显卡,结果插不进你十年前的主板一样。整个开发团队都被这个兼容性问题卡得死死的,每天的工作就是盯着一堆红线发愁。每次有人提交代码,CI(持续集成)就开始抽风,时不时给你一个奇怪的编译错误,谁也不知道原因。那个老版本的TS,在项目里简直就是个“魔物”,每次都得请假,求爷爷告奶奶才能让它正常运行,太折磨人了。
我当时看着手里的活儿,想着,再不把它彻底解决掉,我们迟早要被这个版本拖垮。我可不想天天处理这种无厘头的破事。于是我拍了桌子,决定当那个“退魔少女”,把这个老版本给彻底升级掉。
升级之路:版本大全的诞生
说干就干,我第一步是想直接跳到最新的稳定版,当时应该是TS 5.x了。我心想一步到位,省事。结果?呵呵,我差点把整个项目搞瘫痪。
我只是改了一下里的版本号,然后跑了一下安装,项目启动瞬间,一万多个报错砸在我脸上。那些以前能跑的代码,现在全说我类型不对、配置不对、模块引用不对。尤其是那个strict模式,以前我们是关着的,现在新版默认开启,它把我们那些粗糙的类型定义全都扒了个精光,简直不留活路。
那一周,我天天对着屏幕挠头,感觉头发都快掉光了。我发现,直接跳级是行不通的,这个项目太大了,牵扯到的依赖也多。没办法,我只能采取最笨也最稳妥的方法:分版本、小步快跑,把每一步的改动都记录下来,这才有了这个“版本大全”的实践记录。
我的升级记录基本上是这样记下来的:
- 第一战:TS 3.8到4.0。 这步主要是适应了新的可选链操作符()和空值合并运算符()。代码风格上做了点统一,把很多
if (a !== null && a !== undefined)这种屎山代码用新的语法替换了。编译速度提升了一点点,但问题不大。 - 第二战:4.0到4.5。 这是个小坎,主要处理了
target和module的配置问题。我们以前用的CommonJS,现在我想逐步转向ES Modules。光是配置文件的修改,我就花了整整两天,跟Webpack那个老头子吵架。 - 第三战:4.5到4.9。 重灾区来了。这个版本对泛型的处理严格了很多。我们项目里大量使用了各种奇形怪状的泛型,很多地方都是以前为了偷懒随便写的
any。我不得不把那些any全都揪出来,一个一个手动定义正确的接口。这工作量,简直就是地狱里的洗碗工。 - 第四战:4.9到5.2(最终决战)。 这一步,我终于敢把
strict: true打开了。打开的一瞬间,新的报错雪花一样飘下来,但数量已经比第一次尝试少了一半。剩下的问题,大多是第三方库的类型定义文件()没跟上,我只能自己写临时的声明文件打补丁,或者干脆降级那些库。
退魔少女的最终实现与心得
前后折腾了一个多月,我才算是把这个老系统从那个黑暗的年代拉到了现代。你们知道我怎么判断成功了的吗?不是编译通过了,而是我把CI的编译时间生生砍掉了一半。以前跑一次编译要五分钟,现在两分钟多一点就搞定,速度那叫一个飞快,感觉机器都轻快了。
现在我们用的就是最新的TS 5.x系列。新功能能用了,新的依赖也能兼容了。最重要的是,以前那些鬼鬼祟祟的类型错误,现在在编写代码的时候,编辑器就能给我指出来,不用等到运行或者编译的时候才发现。这种安稳,是以前完全不敢想的。
回头看这个过程,真的就是一场退魔大战。你不能指望一刀就把魔王砍死,你得一步一步,先清小怪,再破防,才能解决它。这种老项目的升级,急不来,必须得把每一步的改动都吃透了,才能往前走。现在谁要再跟我提什么版本升级,我能立马掏出我的“版本大全”给他们参考。虽然过程痛苦,但看着现在项目跑得这么稳当,值了,太值了。