
最近在瀏覽各個技術平台的前端熱門文章,發現大家正陷入一種極其普遍的技術焦慮裡。
只要 Rsbuild、Rolldown 或者是某個底層的 Rust 構建工具發了新版本,必然會有一批文章曬出 benchmark 跑分圖:冷啟動零點幾秒,熱更新一百多毫秒,全方位碾壓過去的 Webpack,甚至早期的 Vite。
甚至在很多團隊的半年 OKR 規劃裡,技術負責人們為了湊出一個亮眼的工程優化指標,硬生生把 遷移最新構建工具,提效 50% 寫了進去。
圈子裡正在蔓延一種極其危險的錯覺:似乎只要把底層的打包引擎換成帶 Rust/Go 基因的新玩意兒,前端工程化所有的歷史包袱和效能痛點就能迎刃而解🤔。
作為這兩年親自下場,主導過數十萬行程式碼的巨石應用改造的老兵,我今天想跟你們聊一聊這個話題。
先聽我一句勸:如果你們公司的業務線還能正常迭代,冷啟動沒有卡到讓你想砸鍵盤的地步,千萬別再瞎折騰換打包工具了🤷♂🤔。
在 Demo 階段,換個打包工具確實就是改一下 package.json 裡的命令,把設定檔換個名字。
但在真實的商業級工程裡,這叫排雷。
現代前端工程最大的毒瘤,是我們強依賴的那些良莠不齊的 npm 套件。Webpack 為什麼慢?因為它背負了前端十多年的歷史債務,它內部有極其寬容的降級機制和模組解析策略。你塞給它一段極不規範的程式碼,它能連滾帶爬地幫你打成產物。
而那些追求極致效能的現代 ESM 構建工具鏈,底層邏輯往往是非常嚴苛的靜態分析。
我給你舉一個我們在真實專案中踩過的深坑。
業務裡有一個極度複雜且年代久遠的 Excel 純前端匯出依賴,由於作者當年為了相容伺服器端同構,在原始碼的深處寫了這種邏輯:
javascript 體驗AI代碼助手 代碼解讀複製代碼// 某個老舊依賴程式碼
let streamLib;
try {
// 純瀏覽器端根本不需要它,但在做靜態分析時會報錯
streamLib = require('stream');
} catch (e) {
streamLib = null;
}
在 Webpack 時代,我們在設定裡加一句 fallback: { stream: false } 就天下太平了。
但當我們試圖把這個幾萬個模組的專案遷移到要求嚴格的極速構建工具時,災難降臨了。新的底層引擎在處理 CommonJS 向 ESM 轉換(比如依賴 Rollup 的 resolve 機制)時,死活繞不過這種動態注入的模組。
為了讓本地那句 冷啟動 0.8s 的提示跑通,團隊不得不花了好幾天時間,在新的設定裡寫下了一堆極其彆扭、極易失效的補丁:
javascript 體驗AI代碼助手 代碼解讀複製代碼// 為了強行抹平歷史債務,手寫的設定
export default defineConfig({
resolve: {
alias: {
// 強行塞入瀏覽器端的 polyfill 去填坑
stream: 'stream-browserify',
buffer: 'buffer/'
}
},
build: {
rollupOptions: {
// 排除外部依賴,生怕影響線上的其他模組
external:['fsevents', 'crypto']
}
}
})
本地確實跑綠了,但代價是什麼?
是構建產物裡混入了我們根本無法完全掌控的 polyfill。
你確定底層切換後,Tree Shaking 還會按原先的邏輯安全剔除無用程式碼嗎?你確定 CSS 抽取外掛的 Hash 生成策略,不會導致線上某兩個按鈕的樣式發生極其偶發的覆蓋嗎?
如果沒有覆蓋率極高的 E2E 自動化測試兜底,在成熟業務裡換底層打包工具,就是在賭命。
我們理性地算一筆帳。
你們團隊投入了兩個高級前端,看了無數篇文件,提了各種 Issue,用各種 Hack 手段處理了 50 多個依賴衝突,終於用兩週時間完成了新打包工具的遷移。
你們的收益是什麼?是開發每天本地啟動專案時,少等了那 10 秒鐘;是 CI 流水線打包時,快了那 1 分鐘。
但這帶來的隱性成本是極其恐怖的:
線上跑著的核心業務,它的地基被換了,任何一個沒測到的邊緣情境都可能引發 P0 級白畫面。
商業公司的技術迭代是有成本邊界的。老闆發薪水,是讓你去解決複雜的業務流轉邏輯、去提升 C 端使用者的首屏體驗,而不是讓你為了開發環境快那幾秒鐘,去把整個技術地基挖開重新填一次。
當然,如果你的專案冷啟動真的要 3 分鐘,熱更新儲存一下要等 10 秒,那確實嚴重阻礙了產出。
但這根本不是打包工具的錯,這是架構的錯。
編譯慢的本質,是你們把所有的爛程式碼、重型函式庫、甚至不同業務線的邏輯,全部揉在了一個單體巨石應用裡。
解決這個問題最乾淨、最徹底的方法,是業務解耦與物理隔離,而不是去奢求一個跑得更快的編譯器。
老老實實把專案拆了。把幾萬年都不改的底層元件庫、富文字編輯器引擎、大型第三方黑盒依賴,抽離成獨立的 npm 套件,或者引入 Monorepo(比如 pnpm workspace)做物理級別的隔離與預編譯。
json 體驗AI代碼助手 代碼解讀複製代碼// 物理級別的架構解耦,才是解決構建慢的終極手段
{
"name": "company-core-monorepo",
"workspaces":[
"packages/shared-utils", // 純邏輯,預編譯好,主專案無需二次解析
"packages/heavy-components", // 靜態重型元件,隔離打包
"apps/main-admin" // 真正的業務外殼,極其輕量
]
}
當你把業務主幹剝離乾淨,讓應用層只保留純粹的視圖拼接和輕量狀態流轉時。你會發現,哪怕你用著最老舊的 Webpack,專案的冷啟動和熱更新照樣是秒開。
在如今這個技術迭代快到讓人窒息的年代,保持定力,是一個資深開發者最稀缺的品質。
對新工具保持敏感度絕對是好事,你在新開的個人專案裡,盡情去享受 Rust 引擎帶來的極致速度,那是一種享受。
但面對跑著幾萬、幾十萬日活的商業專案,請保持絕對的敬畏。工具終究只是個錘子,別天天糾結你手裡的錘子是鐵打的還是什麼,先看看眼前業務 BUG 修好了沒有🤷♂️。
在複雜的工程世界裡,技術選型的第一鐵律永遠是:
只要沒壞,就不要改🫡