點「上傳」,開發者工具彈出一行紅字:主包大小超過 2MB 限制。版本發不出去,需求還在排隊。
這篇文章不講原理大全,只按「省得多、改得少」的順序,列出能把主包壓回 2MB 以內的辦法。
先搞清楚:2MB 到底卡的是什麼
微信對小程式代碼包有兩條硬上限:
| 限制 | 上限 |
|---|---|
| 主包(含 app.js、tabBar 頁面、公共資源等) | 2MB |
| 單個分包 | 2MB |
| 整個小程式所有包加起來 | 20MB |
注意這裡的「大小」是上傳後的代碼包體積,不只是 JS。專案目錄裡所有會被打包進去的檔案都算:.js、.wxml、.wxss、.、字體、音訊,以及——通常佔大頭的——圖片。
這是一條「壓不下去就發不出版本」的剛性限制,不是「優化一下更好」。所以第一步不是動手,而是先看清錢花在哪了。
第 0 步:看清主包裡是什麼在佔地方
微信開發者工具右上角「詳情 → 基本資訊」能看到本地代碼包的總大小;更細的在工具列的 「代碼依賴分析」,能按檔案列出體積,並標出哪些檔案沒有被任何頁面引用。
大多數專案看完這張表都會發現同一件事:圖片、字體這類靜態資源比業務代碼大得多。JS 寫幾萬行也就幾百 KB,一張沒處理過的 banner 圖就可能上百 KB,一個 images/ 目錄輕鬆吃掉主包一半。
下面按收益從大到小排。
辦法一:刪掉根本沒用的檔案
最省事,也最常被忽略。
- 「代碼依賴分析」裡標記為未引用的檔案:歷史改版留下的舊圖示、廢棄頁面、測試圖,直接刪。
- 不該進包的檔案:設計稿、README、
.psd、原始素材、Mock 資料。能挪出專案目錄就挪出去;挪不了的,在project.config.裡用packOptions.ignore排除:
{
"packOptions": {
"ignore": [
{ "type": "folder", "value": "design" },
{ "type": "suffix", "value": ".psd" }
]
}
}
- 元件庫全量引入:只用了三個元件卻把整個 UI 庫打進主包,是常見的「隱形胖子」。改成按需引入,只保留用到的元件目錄。
辦法二:分包——把不是首屏的頁面挪出主包
這是官方給的正解。主包只留啟動頁、tabBar 頁面和它們真正需要的公共代碼,其餘頁面按業務拆進分包:
{
"pages": ["pages/index/index", "pages/mine/mine"],
"subPackages": [
{ "root": "packageOrder", "pages": ["list/list", "detail/detail"] },
{ "root": "packageActivity", "pages": ["index/index"] }
],
"preloadRule": {
"pages/index/index": { "network": "all", "packages": ["packageOrder"] }
}
}
幾個要點:
- 資源跟著頁面走:分包頁面自己用的圖片、元件,要放進分包目錄。放在主包的公共
images/裡,分包再怎麼拆,體積還是算在主包頭上。 preloadRule分包預下載:進首頁時順手把下一步大概率會用到的分包拉下來,使用者點進去時基本感覺不到延遲。- 獨立分包(
"independent": true):適合活動頁、落地頁這類可以脫離主包單獨打開的頁面,啟動時不依賴主包。 - 分包非同步化:跨分包引用元件或 JS 時,可以用佔位元件和
require.async,避免為了「共用」把代碼又提回主包。
分包的代價是要改目錄結構和跳轉路徑。老專案拆起來工作量不小,這也是為什麼值得先把下面這一步做了——很多時候,做完它主包就已經回到 2MB 以內了。
辦法三:把圖片壓小(改動最小、收益最直接)
圖片是主包裡最容易「白白胖著」的部分。設計師導出的 PNG 帶著多餘的資料區塊,JPG 用的是偏高的品質參數,這些位元組使用者看不出來,但每一個都計入 2MB。
哪些圖必須留在包裡
不是所有圖都能挪到 CDN:
- tabBar 圖示:
iconPath/selectedIconPath只能是本地路徑,不支援網路圖片。 - 啟動頁、首屏 Logo、兜底佔位圖:弱網或離線時也得能顯示。
- 頻繁出現的小圖示:每次都走網路請求並不划算。
這些圖只能留在包裡,所以唯一的辦法就是讓它們變小。
順帶避開一個坑:wxss 裡的背景圖
在 .wxss 裡用 background-image 引用本地圖片,真機上是不生效的,常見的繞法是轉成 base64 內聯。但 base64 編碼會讓體積膨脹約三分之一,而且它藏在樣式檔案裡,依賴分析裡不太顯眼。能改成 <image> 元件或網路圖片的,就別內聯;非內聯不可的,先把原圖壓小再轉。
用 ImgZilla 原地壓縮整個資源目錄
做小程式最怕的,是壓完圖還要回頭改路徑——wxml 裡、wxss 裡、JS 配置裡、tabBar 配置裡,到處是 /images/xxx.png。很多壓縮工具會另存為 xxx-min.png,或者要求你匯出到別的資料夾,壓完還要手動替換、再核對一遍有沒有漏。
ImgZilla 是一款 macOS 圖片壓縮工具,它的做法是原地壓縮:
- 檔名、路徑、格式都不變。
icon-home.png壓完還是icon-home.png,代碼裡一行引用都不用改。 - 整個目錄拖進去即可。它會遞迴掃描所有子目錄,並自動跳過隱藏檔案和
node_modules。把images/、static/甚至整個專案目錄拖進去,壓完直接在開發者工具裡看包體積變化。 - 原圖預設移入廢紙簍,覺得哪張不滿意,右鍵「放回原處」即可還原。專案在 Git 裡的話,還多一層保險。
- 全程本地執行,不聯網上傳。公司專案的素材不會離開你的電腦,也沒有在線壓縮站那種單次張數、單張大小的限制。
畫質方面,不同格式的處理方式不一樣,這裡說清楚:
- PNG:使用 oxipng 做無損壓縮,像素一點不變。小程式裡大量的圖示、切圖都是 PNG,這部分可以放心壓。
- SVG:清理冗餘標記,也是無損的。
- JPG / WebP / GIF 等:屬於有損重編碼,參數已經針對每種格式調校在視覺無損區間——肉眼難以察覺差異。不必憑信任接受,內建對比視窗(
⌘D)可以左右分螢幕、放大到實際像素逐張對照。
另外兩點也值得知道:
- 已經壓到頭的圖它不會硬壓:壓縮率不足 0.4% 的檔案會標為「已最小」,原樣保留,不會為了數字好看把圖搞糊。所以如果你的圖之前已經被認真優化過,這一步能省的可能不多——這很正常,也說明該轉向分包了。
- 它不轉格式、不改解析度。PNG 壓完還是 PNG,尺寸不變。如果你想把圖換成 WebP,或者把 3 倍圖降成 2 倍圖,那是另一件事,需要另外處理。
能省多少,取決於圖原來是怎麼導出的,我們不給一個籠統的百分比。作為參考,我們做過兩次公開實測:一批網站入庫時已經壓過一遍的 JPG,再壓一次仍然省了 46.8%;一批相機直出的 JPG 省了 76.8%(詳見《ImgZilla 實測》系列)。小程式裡的圖往往是設計工具直接導出的,通常沒被認真壓過,所以值得先跑一遍看看。
ImgZilla 目前只有 macOS 版(macOS 12.3 及以上),Mac App Store 可下載,每天可免費壓縮 10 張。用 Windows 開發的同學,這一節的思路同樣適用,換一個同樣能「保留原檔名」的工具即可。
辦法四:把大圖挪到 CDN
壓小之後仍然很大的圖——比如活動 banner、詳情頁長圖、商品大圖——本來就不適合放在包裡。上傳到物件儲存或 CDN,代碼裡換成網路地址,主包立刻輕一截。
但這不是免費的:
- 首次載入要走網路,弱網下會有空白期,最好配合佔位圖或骨架螢幕。
- 要維護一套上傳、快取、更新的流程。
- CDN 按流量計費。圖片每被載入一次都在花錢,流量費約等於「圖片體積 × 訪問次數」。所以挪到 CDN 之前,同樣值得先把圖壓小——壓一次,之後每一次訪問都在省流量。
辦法五:代碼層面的收尾
圖和結構處理完,剩下的零碎空間可以從代碼裡摳:
- 開發者工具「詳情 → 本地設定」裡勾選上傳時壓縮腳本、樣式、WXML。
app.開啟"lazyCodeLoading": "requiredComponents"(按需注入)。它主要改善的是啟動速度而不是包體積,但既然在做效能優化,一起開上。- 檢查
miniprogram_npm:構建出的 npm 包是否有整包引入卻只用了一兩個函數的情況。 - 大段寫死在 JS 裡的靜態資料(城市列表、配置表)考慮改為介面下發。
一張表總結
| 辦法 | 能省多少 | 要改多少 | 建議順序 |
|---|---|---|---|
| 刪除無用檔案 | 看專案歷史包袱 | 很少 | 1 |
| 原地壓縮圖片 | 圖片越多、導出越隨意,省得越多 | 幾乎為零,路徑不變 | 2 |
| 分包 | 可以很多 | 目錄和路由都要動 | 3 |
| 大圖挪 CDN | 很多 | 要改引用、維護上傳流程 | 4 |
| 代碼壓縮與按需引入 | 較少 | 視情況 | 5 |
順序的邏輯很簡單:先做改動小的,再做改動大的。刪檔案和壓圖片幾乎不碰業務代碼,做完再看主包還差多少——如果已經回到 2MB 以內,今天的版本就能發;如果還不夠,再動分包和 CDN,心裡也有數了。
主包超限往往發生在上線前最緊張的時候。把圖片壓縮放進日常流程——每次加新圖之前先壓一遍——下次就不用在截止日前對著那行紅字發愁了。
