Appearance
建材仓库的大扫除
背景:node_modules 积压 8 个高危漏洞,开发环境暗藏风险。
过程:升级 TypeScript/ESLint/Vite/Babylon.js。
联邦的城邦都建在地上。
渲染城的高塔、物理城的钟楼、议会城的阶梯、图书馆城的书架——每一座城都光鲜亮丽。但很少有人去想:这些城,是用什么建起来的?
答案在仓库里。
在联邦领地的角落,有一座巨大的仓库,名叫 node_modules。里面堆满了各种建材——砖头、水泥、钢筋、玻璃、电线、管道……每一块砖头上都刻着一个版本号。
城邦的一砖一瓦,都来自这座仓库。
仓库越大,越容易藏污纳垢。
第一张清单:八颗红痣
"体检不能只看城邦本身,"外交官站在仓库大门前,手里拿着一张纸,"建材也得检查。"
"建材?"AI 同行者看着面前如山的箱子,"这些……不都好好的吗?"
"看上去好好的,"外交官摇摇头,"但你不知道哪块砖头里面有裂纹,哪袋水泥已经过期了,哪根钢筋里面生了锈。"
他举起手里的纸——那是一份 npm audit 报告。
"八个漏洞,"他说,"七个高危,一个中危。"
AI 凑过去看:
esbuild <=0.24.2
Severity: moderate
esbuild enables any website to send any requests to the development server...
minimatch 9.0.0 - 9.0.6
Severity: high
minimatch has a ReDoS via repeated wildcards..."等等,"AI 挠头,"这些不是我们直接用的吧?esbuild 是 Vite 带的,minimatch 是 ESLint 带的……"
"对,都是传递依赖,"外交官点头,"但传递依赖也是依赖。就像你建房子,砖头是从建材商那里买的——砖坏了,墙一样会塌。"
"可这些都是开发环境用的啊,"AI 辩解,"生产构建又不带进去。"
"确实,生产环境不受影响,"外交官说,"但开发环境也是环境。你每天 npm run dev,开发服务器就跑在你电脑上。如果有个漏洞能让恶意网站读取你本地的文件——你觉得危不危险?"
AI 打了个寒颤。
"而且,"外交官补充,"现在是 8 个,不升级的话,以后会变成 18 个、80 个。债是会利滚利的。"
他把纸折起来:"大扫除吧。从最容易的开始。"
第一块砖:Babylon.js 小版本升级
最简单的是 Babylon.js。
同大版本内的小版本升级——就像换了同品牌同型号的新砖头,尺寸、重量、接口全都一样,只是修了些小裂纹。
bash
npm install @babylonjs/core@^9.14.0命令敲下去,几秒钟就完事。
"搞定,"AI 拍了拍手,"这也太简单了。"
"简单的事情先做,"外交官说,"省得放在那里心烦。"
验证也很简单——tsc --noEmit 过,vite build 过,完事。
第一块砖,轻松换掉。
第二块砖:TypeScript 大版本升级
接下来是 TypeScript。
从 4.9 升到 5.4——跨了一个大版本。这就像把砖头从「标准砖」换成「高精度砖」,尺寸更精准、强度更高,但你得确认所有墙的尺寸都还对得上。
"听说 TypeScript 5 有很多 breaking changes?"AI 有点担心。
"还好,"外交官说,"TypeScript 的大版本升级通常比较温和。主要是类型收严,很少有语法级别的 breaking change。"
他敲下命令:
bash
npm install --save-dev typescript@^5.4.0然后跑 tsc --noEmit。
屏幕上跳出来一行红:
src/motion/beat-detector.ts(99,44): error TS2345:
Argument of type 'Uint8Array<ArrayBufferLike>' is not assignable
to parameter of type 'Uint8Array<ArrayBuffer>'."看,"外交官指着屏幕,"类型收严了。Uint8Array 默认是 ArrayBufferLike(包含 SharedArrayBuffer),但 getByteFrequencyData 只接受普通 ArrayBuffer。"
"就这一个错误?"AI 有点意外。
"就一个,"外交官点头,"显式声明一下类型就好。"
typescript
private freqData: Uint8Array<ArrayBuffer> = new Uint8Array(0);改完再跑——一片绿。
"就改了一行?"AI 不敢相信。
"就一行,"外交官笑了,"TypeScript 团队的向后兼容性做得很好。大版本升级也没那么可怕。"
第二块砖,有惊无险。
第三块砖:ESLint 大版本升级 + flat config
真正的硬骨头是 ESLint。
从 8 升到 9——这不仅仅是换砖头,连砌墙的方式都变了。ESLint 9 默认使用 flat config(扁平配置),取代了用了多年的 .eslintrc 格式。
"这就像……"AI 想了想,"之前的配置文件是层层嵌套的积木塔,现在要全部推倒,平铺成一层?"
"差不多那个意思,"外交官说,"旧的配置是继承链——extends 来 extends 去,像族谱一样。flat config 就是个数组,每个元素是一个配置对象,按顺序应用,简单直接。"
他删掉旧的 .eslintrc.cjs,新建了 eslint.config.js。
"好处是更灵活、更可预测,"他一边写一边说,"坏处是——所有插件都得升级到支持 flat config 的版本。"
这意味着一堆东西都要跟着升:
@typescript-eslint/eslint-plugin6 → 8@typescript-eslint/parser6 → 8eslint-config-prettier8 → 10eslint-plugin-vitest—— 等等,这个还没 1.0
"vitest 插件最新才 0.5.4,"AI 查了一下,"不支持 ESLint 9?"
"对,还没跟上,"外交官说,"那就先去掉吧。反正它主要是给测试文件加规则,影响不大。以后出了 1.0 再加回来。"
卸掉一个包袱,剩下的一口气升上去。
然后是写 flat config。
旧配置里有 ban-types 规则,到了 v8 改名成 ban 了。一开始报错找不到规则,查了 changelog 才知道改名了。
"这就是大版本升级的麻烦,"外交官一边改一边说,"规则会改名、会移除、会合并。你得一个个对照着改。"
globals 也变了。旧配置里 env: { browser: true, es2022: true } 一行搞定,flat config 里得自己引入 globals 包,然后展开。
"多了一步,"AI 说,"但也更清晰了。"
"对,"外交官点头,"什么都明明白白写在那里,没有黑魔法。"
折腾了半个钟头,配置文件终于写好了。
跑一下 eslint src——
✖ 2331 problems (0 errors, 2331 warnings)"0 errors!"AI 松了口气,"warnings 和之前差不多。"
"那就好,"外交官也松了口气,"warnings 是正常的,我们的规则本来就比较松。只要没新增 error 就行。"
第三块砖,啃下来了。
第四块砖:Vite 大版本升级
最后一块是 Vite。
从 5 升到 6——构建工具的大版本升级,通常是最让人担心的。毕竟构建工具是整个项目的地基,地基动了,上面的一切都可能受影响。
"要不要先做个备份?"AI 有点紧张。
"不用,"外交官很淡定,"有 git 呢。大不了回退。"
他敲下命令:
bash
npm install --save-dev vite@^6.0.0然后屏息凝神,敲下:
bash
npx vite build屏幕上开始滚动构建日志。
dist/index.html 4.41 kB
dist/assets/index_bg-y0ZN0FV8.wasm 546.19 kB
dist/assets/index-C-B0-siL.js 575.63 kB
(!) Some chunks are larger than 500 kB after minification...
✓ built in 1.25s"成了?"AI 不敢相信。
"成了,"外交官也有点意外,"这么顺利?"
"Vite 6 的 breaking changes 主要是 SSR 和插件 API 那边,"AI 查了一下,"对纯客户端应用影响不大。"
"那正好,"外交官笑了,"省了不少事。"
验证完构建,再验证开发服务器——vite 能正常启动,热更新正常,一切正常。
第四块砖,意外地顺利。
归零
四块砖都换完了。
外交官再次拿起 npm audit 报告,看了一眼——
found 0 vulnerabilities"零,"他把纸递给 AI,"八个漏洞,全没了。"
AI 接过纸,盯着那个大大的 0 看了好一会儿。
"就这么……搞定了?"他说,"感觉也没多难啊。"
"是不难,"外交官说,"难的是开始。"
"什么意思?"
"很多项目拖着不升级依赖,"外交官把纸折好,"总觉得'以后再说',总觉得'现在能用就行'。结果越拖越多,最后变成不敢碰的屎山——一碰就碎。"
"我们这不是碰了吗?"AI 说,"也没碎啊。"
"因为我们升得勤,"外交官说,"小步快跑,每次只升一两个,出了问题也好定位。攒一年再升,那才叫酸爽。"
他拍了拍 AI 的肩膀:
"记住——技术债的利息是复利的。 越早还,越省力。"
仓库的新规矩
大扫除结束后,外交官在仓库门口贴了一张新规矩:
【建材维护须知】
1. 每月跑一次 `npm run audit`
——发现高危漏洞,及时处理
2. 每季度跑一次 `npm run outdated`
——看看哪些依赖过期了,择机升级
3. 小版本随时升
——同大版本内,风险低,收益稳
4. 大版本攒够了再升
——一次升一堆,比分散升省事
5. 升完跑三件事
——tsc 检查、build 构建、lint 过一遍"就这些?"AI 看着纸条。
"就这些,"外交官说,"简单的规矩,坚持执行,比什么都强。"
仓库里,阳光透过天窗照进来。
一排排货架整齐排列,每个箱子上都刻着最新的版本号。
没有裂纹,没有锈迹,没有过期。
干净,清爽,随时待命。
这就是大扫除的意义——
不是为了完美,
而是为了心里踏实。
番外附记:升级成本 vs 收益
| 升级项 | 版本跨度 | 改动量 | 风险 | 收益 |
|---|---|---|---|---|
| @babylonjs/core | 9.13 → 9.14 | 0 行代码 | 🟢 极低 | bug 修复、小特性 |
| TypeScript | 4.9 → 5.4 | 1 行代码 | 🟢 低 | 新语法、性能改进、类型更准 |
| ESLint + @typescript-eslint | 8/6 → 9/8 | 新配置文件 | 🟡 中 | flat config、修复 7 个 high 漏洞 |
| Vite | 5 → 6 | 0 行代码 | 🟢 低 | 修复 esbuild 漏洞、构建更快 |
总耗时: 约 1 小时 总改动: 约 150 行(主要是新配置文件) 漏洞数: 8 → 0 ✅
番外二完。地下管道检修了,建材仓库也打扫了。联邦从里到外,干干净净。