你的小程序在真機(jī)調(diào)試?yán)锸灼良虞d超過 3 秒,流失的用戶不是比例,而是后續(xù)所有變現(xiàn)機(jī)會(huì)——微信官方數(shù)據(jù)顯示,加載超過 3 秒的頁面,用戶跳出率會(huì)達(dá)到 37.5%。更麻煩的是,這類性能問題往往只在特定機(jī)型、弱網(wǎng)或冷啟動(dòng)時(shí)暴露,本地預(yù)覽根本復(fù)現(xiàn)不了。

啟動(dòng)慢本質(zhì)上是三件事在同時(shí)作祟:主包體積過大、啟動(dòng)階段同步 API 調(diào)用過密、頁面節(jié)點(diǎn)數(shù)膨脹導(dǎo)致第一次渲染耗時(shí)過高。許多團(tuán)隊(duì)把優(yōu)化動(dòng)作放在上線前夕,用“刪幾張圖、合幾個(gè)請(qǐng)求”的思路去救火,結(jié)果主包反而因?yàn)殄e(cuò)誤引入的基礎(chǔ)庫越來越大。

為什么你的小程序總在“白屏”

微信小程序的啟動(dòng)過程可以拆成四個(gè)嚴(yán)格串行階段:

  1. 下載代碼包:微信客戶端從 CDN 拉取主包和增量更新。主包超過 2 MB,就要面對(duì)運(yùn)營商限速和弱網(wǎng)重傳。
  2. 代碼注入:邏輯層和渲染層分別注入 JavaScript 代碼,這個(gè)階段主線程被阻塞。
  3. App.onLaunch → Page.onLoad:此時(shí)同步調(diào)用的 wx.getStorageSync、wx.getSystemInfoSync 會(huì)直接卡住整個(gè)啟動(dòng)流程。
  4. 頁面初次渲染:WXML 節(jié)點(diǎn)數(shù)越多、setData 傳遞的數(shù)據(jù)量越大,首幀越慢。

大多數(shù)優(yōu)化文章會(huì)提示你使用分包,但很少告訴你:分包體積并不直接影響啟動(dòng),影響啟動(dòng)的永遠(yuǎn)是主包。錯(cuò)誤的分包策略會(huì)把公共依賴強(qiáng)行打進(jìn)主包,反而讓主包體積失控。

用工程化手段擠壓啟動(dòng)耗時(shí)

1. 重新劃分主包邊界

主包里只保留立即使用的東西:首頁及其直接依賴的組件、必要的公共模塊(如網(wǎng)絡(luò)攔截器、全局樣式)。其他頁面、非首屏組件、工具庫全部遷入分包。

檢查主包真實(shí)體積的最佳工具不是開發(fā)者工具的體積條,而是代碼包分析報(bào)告:在開發(fā)者工具中打開“詳情 → 代碼依賴分析”,查看主包內(nèi)每個(gè) JS 文件的貢獻(xiàn)。你會(huì)發(fā)現(xiàn)在 node_modules 里某個(gè)庫的 Polyfill 或者并未使用的大段圖表配置也被打包進(jìn)來了。

示例 app.json 的基礎(chǔ)分包配置:

{
  "pages": [
    "pages/index/index"
  ],
  "subPackages": [
    {
      "root": "packageOrder",
      "pages": [
        "list/list",
        "detail/detail"
      ]
    },
    {
      "root": "packageUser",
      "pages": [
        "profile/profile"
      ],
      "independent": true
    }
  ],
  "preloadRule": {
    "pages/index/index": {
      "network": "wifi",
      "packages": ["packageOrder"]
    }
  }
}

配置的關(guān)鍵約束:

  • independenttrue 的分包不依賴主包,適合低頻功能(如個(gè)人中心),能顯著降低主包風(fēng)險(xiǎn)。
  • preloadRulenetwork 限定為 wifi 可以避免移動(dòng)網(wǎng)絡(luò)下自動(dòng)預(yù)加載帶來的流量投訴,同時(shí)讓用戶在 Wi-Fi 下獲得無縫跳轉(zhuǎn)。

2. 消除啟動(dòng)路徑上的同步 API

App.onLaunch 和首頁 Page.onLoad 中所有帶 Sync 后綴的 API 都是定時(shí)炸彈。getSystemInfoSync 在一臺(tái)低端機(jī)上可能花掉 40-60 ms,但更隱蔽的是 getStorageSync——如果存儲(chǔ)了較大 JSON 字符串,阻塞時(shí)間會(huì)成倍增加。

修改原則:

  • 將非必須的同步操作后移到 onReady 之后。
  • 無法后移的讀取存儲(chǔ)改用異步版本,并在代碼分支中處理緩存缺失的等待邏輯。
  • 全局狀態(tài)初始化只保留最小字段,其余按需拉取。
// 改造前:App.onLaunch 內(nèi)堵塞
const systemInfo = wx.getSystemInfoSync();
const token = wx.getStorageSync('userToken');
this.globalData.system = systemInfo;
this.globalData.token = token;

// 改造后:異步獲取關(guān)鍵項(xiàng),延時(shí)獲取非必要信息
App({
  onLaunch() {
    wx.getSystemInfo({
      success: (res) => {
        this.globalData.system = res;
      }
    });
    // token 僅用于后續(xù)網(wǎng)絡(luò)請(qǐng)求,不需要在啟動(dòng)時(shí)阻塞
    this.globalData.tokenPromise = new Promise((resolve) => {
      wx.getStorage({
        key: 'userToken',
        success: ({ data }) => resolve(data),
        fail: () => resolve(null)
      });
    });
  }
})

3. 縮減首次渲染成本

微信小程序的渲染線程和邏輯線程分離,setData 的數(shù)據(jù)會(huì)通過序列化、跨線程傳輸,再觸發(fā) diff 和 patch。首次 setData 攜帶的數(shù)據(jù)超過 64 KB,就會(huì)進(jìn)入微信平臺(tái)的性能監(jiān)控線。

落地操作:

  • 首屏只 setData 可視區(qū)域所需的最少字段,而不是一次傳入整個(gè)接口返回體。
  • 使用骨架屏替代加載中 Spinner,骨架屏的代碼應(yīng)內(nèi)嵌在 WXML 中,利用 CSS 動(dòng)畫控制,而不是通過 wx:if 再次觸發(fā) setData。
  • 列表首屏強(qiáng)制使用“限定條數(shù) + 分頁”,避免一次 setData 傳入幾百條數(shù)據(jù)。

什么時(shí)候不該優(yōu)化

并非所有項(xiàng)目都需要壓啟動(dòng)耗時(shí)。如果你的小程序?qū)儆诠ぞ咝汀⒂猛昙醋?,并且用戶留存主要不依賴首頁加載速度(例如臨時(shí)性掃碼進(jìn)入),投入大量人力做分包和異步改造反而會(huì)延長鏈路,增加維護(hù)復(fù)雜度。優(yōu)化啟動(dòng)需要權(quán)衡現(xiàn)有架構(gòu)負(fù)債:已經(jīng)開始大規(guī)模使用 Sync API 的遺留項(xiàng)目,全面改為異步可能引發(fā)時(shí)序 Bug,應(yīng)當(dāng)先用Lint規(guī)則限制新增 Sync 調(diào)用,然后逐步替換啟動(dòng)關(guān)鍵路徑。

另一個(gè)值得警惕的盲區(qū)是“預(yù)加載一切”。preloadRule 設(shè)置了全量分包預(yù)加載后,網(wǎng)絡(luò)較差時(shí)相當(dāng)于把多個(gè)包的下載成本提前集中在首頁,反而延長了可交互時(shí)間。預(yù)加載只應(yīng)對(duì)那些用戶下一步訪問概率大于 70% 的頁面,且必須配合網(wǎng)絡(luò)條件判斷。

從測(cè)量開始,以基線結(jié)束

啟動(dòng)優(yōu)化的第一步不是動(dòng)代碼,而是建立可對(duì)比的性能基線。

  • 使用微信開發(fā)者工具的“性能面板”記錄啟動(dòng)各階段耗時(shí),保存為 JSON 文件。
  • 在不少于 3 款真機(jī)(含 Android 低端機(jī)與 iPhone 6s 級(jí)別)上取 5 次冷啟動(dòng)中位數(shù)。
  • 設(shè)置明確退出標(biāo)準(zhǔn):例如主包 < 1.5 MB、首頁首次 setData 數(shù)據(jù)量 < 30 KB、啟動(dòng)階段同步 API 調(diào)用數(shù)為 0。

將這些指標(biāo)接入你的 CI,在每次合并請(qǐng)求時(shí)跑一次自動(dòng)化性能檢查,避免性能回退。優(yōu)化后的成果不是你口頭匯報(bào)的“感覺快了”,而是把“首頁平均啟動(dòng)時(shí)間”從 3200 ms 降到 840 ms 的硬數(shù)據(jù),這個(gè)數(shù)據(jù)直接影響審核員對(duì)加急審核的判定,也影響運(yùn)營投放時(shí)點(diǎn)擊到激活的轉(zhuǎn)化率。

← 上一篇 舊商城二次開發(fā)前的五個(gè)生死評(píng)估維度 下一篇 → 微信公眾號(hào)開發(fā)的三個(gè)隱性成本,多數(shù)技術(shù)選型時(shí)被你忽略了