雜記 › 技術筆記 › 壓縮成一行的 JS 看不懂?線上美化、格式化程式碼

壓縮成一行的 JS 看不懂?線上美化、格式化程式碼

上個月線上有一個偶發的點擊報錯,堆疊最後一行指著 app.7f3a9c.js:1:45210。我把那個檔案拖進編輯器,1.2 MB 的程式碼全擠在一行,游標按一下右方向鍵要走十幾秒,語法高亮被迫關掉。那天我花了兩個小時才學會讀這種程式碼,也順帶明白一件事:壓縮和美化不是同一個開關的正反兩面,它們能給你的東西和給不了你的東西,其實完全不是一回事。

一、線上程式碼為什麼會變成一行

JS 在發布前通常會被建置工具做三件事:把幾十個模組合成一個 bundle,刪掉所有多餘的空白和換行,再把區域變數名換成單字母(這一步叫 mangle)。目的很直接——減少傳輸體積、減少解析成本。去掉空白和註解一般能讓檔案小 30%–50%,這就是幾乎所有生產建置預設這麼做的原因。

代價是可讀性歸零。於是報錯堆疊裡的行號幾乎都是 :1: 開頭,後面跟一個幾萬的數字。那個數字是字元偏移,不是「第幾萬行程式碼」,很多人第一次看到會以為是行數,然後在一個只有一行的檔案裡翻半天。

二、讀壓縮程式碼的四種做法,和各自的代價

做法能做到什麼侷限
瀏覽器開發者工具裡格式化(Sources 面板的 {})把目前資源在面板裡展開成多行,可以直接設中斷點展開結果只存在於面板中,行號與原檔案對不上;檔案幾十 MB 時面板會明顯卡
編輯器的格式化指令小檔案方便,能存回磁碟繼續搜尋對超過 1 MB 的單行文字很容易假死,有些編輯器超過閾值直接放棄高亮
本機重新建置一份未壓縮產物最接近原始碼,變數名和檔名都在要還原相依套件和建置設定,跑一遍十幾分鐘;線上那份的提交版本對不上就白費工夫
貼到線上格式化工具只做排版,幾秒看到函式巢狀結構不會還原變數名,不能替你修 bug;超大檔案同樣會慢

這裡有個概念要先拆開:美化不等於反混淆。美化只加換行和縮排,function e(t){return t*n+2} 展開一百行也還是 e、t、n。想把這些字母對回原來的名字,只有 source map(同目錄下的 .map 檔案)能做到。

三、我實際的處理順序

  1. 先在堆疊裡拿三個資訊:檔名(含雜湊)、行號、欄號。雜湊能確認報錯的是不是目前線上那份,別在舊產物上找半天。
  2. 看看有沒有 .map。有的話瀏覽器 Sources 面板直接對回原始碼,下面幾步全部省掉。
  3. 沒有 sourcemap 才動排版。把整段貼進 JS 美化壓縮,縮排選 2 空格(同一螢幕能多看幾十行),註解開關打開——有時殘留的 license 標頭恰好告訴你這段來自哪個第三方函式庫,這比省幾行程式碼有用。
  4. 用欄號做定位。美化後行號變了,但字元的相對位置沒變,把 45210 當成大致偏移去找。真正能搜的錨點是字串常值、介面路徑、console 文案和錯誤提示文字——這些是 mangle 唯一不會碰的東西。
  5. 拿到結構再談重現。看到可疑函式後先在本機重現,不要試圖在生產產物上「改回去」:改壓縮產物是假動作,下一次建置就全沒了。

同一天還有另一半問題在樣式上,報錯指向的 CSS 也是一整行,我用 CSS 美化壓縮 展開後才發現是一個被覆蓋的後層疊規則。介面回傳的那串壓成一行 JSON 也順手丟進 JSON 格式化 展開,確認報錯源頭是少了一個欄位,而不是頁面邏輯寫錯——這一步差點讓我白改半天 JS。

四、幾個容易翻車的細節

  • 行號一定會變。美化前是 1:45210,美化後可能變成第 780 行第 12 欄。記錄問題的時候把兩組數字都寫下來,否則第二天自己都對不上。
  • 反向壓縮有風險,風險在分號。去掉空白通常不改變執行結果,但如果原始碼依賴自動分號插入(ASI),換行一刪,兩句就黏成一句,可能直接變成語法錯誤。生產壓縮交給建置工具,別自己手工把程式碼「刪成一行」。
  • 版權註解會被吃掉。以 /*! ... */ 開頭的註解一般要求保留,壓縮前後檢查一遍,有些函式庫缺了這條註解是不合規的。
  • 美化不會修語法。括號本來就不配對的話,加再多換行也救不回來,報錯只會讓你以為「格式化工具壞了」。
  • 壓縮率統計是有用的旁證。把疑似壓縮過的檔案壓一遍,看原始字元數、結果字元數和壓縮率:如果空白幾乎沒得去(變化不到 5%),說明它本來就是壓縮產物,再壓沒有意義;正常未壓縮的原始碼去掉空白和註解的收益往往在三成以上。
  • 程式碼該不該貼進網頁。這類工具是在瀏覽器本機完成處理、程式碼不送到伺服器,但公司內部未公開的程式碼要不要放進任何一個線上頁面,仍要先看團隊的規定,瀏覽器擴充功能和代理日誌這些環節不在工具的控制範圍內。超大檔案(幾 MB)在瀏覽器裡處理也會慢,那時候重新建置一份未壓縮產物更划算。

五、常見疑問

Q:美化會不會改變程式碼邏輯?

不會。美化只增加換行和縮排,不刪字元、不改語意,執行結果與原檔案一致。

Q:為什麼格式化之後跟報錯的行號完全對不上?

因為你原來的行號只有一行,報錯給的是欄位置。展開成幾百行之後行號必然變化,靠字元偏移去估算,或者靠字串內容去搜尋,比靠行號可靠。

Q:能把 a、b、c 這種變數名還原回去嗎?

格式化工具做不到,它只處理排版。還原名字需要建置時產生的 source map 檔案;沒有它,只能靠上下文的字串和呼叫關係猜。

Q:壓縮能省下多少體積?值得專門做嗎?

去空白和註解本身常見收益是 30%–50%。但如果伺服器已經開了 gzip 或 brotli,純空白被壓縮後重複度很高,實際傳輸節省會明顯小於這個數字。真正的大頭在刪死程式碼、按路由拆包和把相依套件打進公共 chunk。

Q:除錯時用什麼版本,上線又該留什麼?

慣例是開發看美化版、生產部署壓縮版,同時把未壓縮產物或 sourcemap 留存下來給錯誤監控平台用。要留意 sourcemap 洩漏的風險:把它上傳到監控服務是可以的,但直接公開在公網等於把原始碼交給別人。

回頭看那兩個小時,真正卡住我的不是「程式碼看不懂」,而是一上來就想把整個檔案讀通。壓縮產物不是給人逐行讀的,它是給機器讀的;人要做的是先用排版把結構立起來,再用字串錨點切進去。有沒有 sourcemap 決定了後面所有做法——有就用它,沒有就美化加錨點,兩條路都比硬盯著那一行強。

📎 相關文章

PNG 壓了三遍只減 426 位元組:無損壓縮的真實邊界 2026-10-22 WebP 轉 PNG 後體積漲了幾倍:什麼時候必須轉,怎麼把漲幅壓住 2026-10-22 截圖縮到四分之一,小字糊成一團的三種原因 2026-10-22 同樣裁 300×300,頭像為什麼總偏右下角半格 2026-10-22 按每月 1.7 倍預估 18 個月,腳本和表格為什麼給出兩個數 2026-10-22