Vue 編譯器重做 source map:省三成 CPU 之外,開發者該留意的是那條破壞性改動
十一月六日,掘金上一篇整理 Vue 核心倉庫討論中 PR 的文章引起前端圈注意。這個由 Vue 核心團隊成員 johnsoncodehk 提出的變更(PR 編號 16126),把編譯器的 source map 換了一套做法,CPU 省掉三成左右,記憶體省掉三分之一,還順手修掉十幾個報錯指錯行的問題。規格表上看不到的,往往才是重點,這個 PR 動的其實是「生成的程式碼怎麼留住出處」這件事本身。
source map 是什麼,為什麼舊做法會出錯
寫 .vue 檔的時候,報錯堆疊要指回第幾行第幾列,瀏覽器開發者工具裡下中斷點也要落在原始檔上,靠的就是編譯時順手生成的一份對照表。可以把它想成裝潢後的房子:你在新格局裡找東西,需要一張「這個櫃子原本在哪個房間」的對照單。沒有這張單子,所有除錯都只能在編譯產物裡摸索。
舊的生成方式有點繞,一邊用 magic-string 改源碼字串,一邊用 source-map-js 另外記一份映射,兩套並排走。平常沒事,一碰上多行運算式、v-for、v-model 這種需要插入程式碼的場景,兩邊就容易對不上。
錯位具體長什麼樣?來源列了幾個例子:多行的 :class,生成的取值會被指到上一行末尾;v-for 那行裡,生成的開始標記和文字轉換會指錯行;v-model 編譯出來的那層指令包裝,在舊版裡根本沒有映射,點了中斷點也跳不到源碼。這類零碎錯位在 PR 裡一共列了十幾個,散落在不同編譯器套件上,這次一起修了。
新做法換了思路:每一段生成程式碼自帶來源範圍,映射直接由片段拼出來,不再另外單獨維護一份對照表。等於是每個搬過去的櫃子直接貼上原房間標籤,而不是事後再畫一張總圖去對。
數字面:省了多少
來源引用的測試數據如下:編譯腳本熱啟動 CPU 從 2988 毫秒降到 2108 毫秒,少了 29%,峯值記憶體從 848 MB 降到 563 MB。冷啟動從 4517 毫秒降到 3575 毫秒,記憶體從 759 MB 降到 445 MB。帶內聯映射的編譯模板少了 37%,解析階段降幅最大,從 491 毫秒降到 277 毫秒。1297 個元件解析完,保留堆從 113.4 MB 降到 42.0 MB。
作者也交代了測試口徑:1297 個 .vue 檔、Node 24、Apple Silicon、只跑一輪。這些是特定環境下的量測結果,不適合直接當跨環境排名看。
安裝體積也跟著小了一點。compiler-sfc 的安裝樹從 12 個套件降到 11 個,source-map-js 從幾個編譯器套件裡一起移除,只剩一個間接依賴還在用。瀏覽器產物方面,compiler-sfc 小 5%,compiler-vapor 小 8%,執行時的全域包基本沒動。
破壞性改動與目前狀態
代價方面,最直接的一條:MagicString 不再從 @vue/compiler-sfc 匯出,PR 裡寫明這是故意的破壞性改動。線上演練場那類工具會受影響,修復已進主分支,等下一個版本發布。另一條是這個改動會進小版本,不是補丁版本。
目前狀態也值得看清楚。這個變更提出後,標題改了兩次,從重構改成效能最佳化。作者第二天重跑生態測試,十六個套件裡還有八個是紅的,nuxt、vue-i18n、vuetify、router 都在其中。作者自己分析原因:一類是快照需要更新,一類是新依賴發布太新,被套件管理器的發布年齡策略擋住裝不上,等版本老一點重跑就行。同一天排程跑的基線是全綠的,所以紅的那部分,確實是這次改動帶出來的。
順帶一提,PR 還修了幾個與映射無關的問題:await await foo() 這種寫法舊版外層拿到空值,現在能拿到正確結果;巨集參數裡寫 await 以前直接報語法錯誤,現在能正常渲染;defineModel 的選項物件以前會被計算三次,現在算一次。
這個 PR 的另一層訊號
還有一個細節挺有意思:分支名裡帶著 cursor 字樣,不少提交把 Cursor Agent 寫成共同作者。一個影響整條 Vue 編譯鏈的改動,一部分提交已經交給 AI 編碼助手來寫,人負責看和改。這與我們先前整理的前端底層換血訊號放在一起讀,方向是一致的:基礎工具鏈的改版節奏正在加快,而 AI 協作已經進到核心框架的維護流程裡。
對不同讀者的實際意義可以分兩段收斂。如果你只寫業務程式碼,短期沒什麼感覺,除錯時行號指得更準、建構快一點,都是無感升級;唯一要做的是升級時留意 MagicString 那條破壞性改動,確認專案沒有直接引用它。要是你在做建構工具、線上演練場,或者自己包了一層編譯器,這個 PR 值得從頭讀一遍,它把「生成的程式碼怎麼留住出處」整個重做了一遍,相關工具的適配時程也該現在就排進規劃。