小程序性能優化要點


指標:

  • 頁面是否能正常訪問?首次內容繪制(First Contentful Paint, FCP)
  • 頁面內容是否有用?首次有效繪制(First Meaningful Paint,FMP)
  • 頁面功能是否可用?可交互時間(Time to Interactive,TTI)

頁面內容是否有用比較主觀。難以規范化數值衡量。

對於小程序

  • 首次內容繪制 FCP = 白屏加載結束 = 頁面開始展示的時間點 - 開始請求時間點(performance.Timing.navigationStart)。在 H5 中就是開始 head 的末尾插入 script,它的執行作為頁面開始展示時間節點。在小程序里可以用 onLoad 生命周期回調觸發時間替代吧?
  • 首次有效繪制 FMP = 首屏渲染完成 = 首屏幕最慢的圖片加載完成時間(或者無圖片就 performance.timing.domContentLoadedEventStart)- performance.Timing.navigationStart。
  • 可交互時間 TTI = performance.timing.domContentLoadedEventStart(頁面元素加載完畢,圖片未必加載完,但可以滾動,搜索操作)時間- performance.Timing.navigationStart。

小程序官方性能指標:

  • 首屏時間不超過 5 秒
  • 渲染時間不超過 500 ms
  • 每秒調用 setData 的次數不超過 20 次
  • setData 的數據再 JSON.stringify 后不超過 256 kb
  • 頁面 WXML 的節點少於 1000 個,節點樹深度少於 30 層,子節點數不大於 60 個。
  • 所有網絡請求都在 1s 內返回結果;

小程序的最終渲染載體仍舊是瀏覽器內核,而非原生客戶端。但給予性能考慮,它啟用了雙線程模型。

webview 線程負責視圖渲染,邏輯層負責執行 JS 代碼。邏輯層通過通過 setData 來傳輸給視圖層數據渲染。然而任何線程間數據傳遞都有延時,所以通信是異步的,視圖層沒法控制具體的 DOM。

小程序加載流程

  • 准備運行環境。啟動雙線程環境,預先加載小程序基礎庫(包括 Webview 的基礎庫和 AppService 的基礎庫,前者注入到試圖中,后者注入到邏輯層)。
  • 下載小程序代碼包。初次啟動需要下載編譯后的代碼包到本地,之后就會緩存代碼。小程序代碼表最終是經過 GZIP 壓縮放在 CDN 上的。
  • 加載小程序代碼包。下載完后執行代碼包,此階段內主包內所有頁面 JS 都會被執行。並做頁面注冊,會調用 JS 里的 Page 構造器來初始化 JS 的數據、方法。
  • 初始化小程序首頁。代碼包加載完畢,基礎庫尋找到首頁,初始化頁面實例,並傳遞信息給視圖層。視圖層結合 WXML、WXSS,初始數據來渲染頁面。

若是首次內容繪制 FCP 時間長,怎么優化?

這通常都是加載的代碼包大引起的,則減小代碼體積

  • 減少小程序代碼包體積,剔除掉冗余代碼,可以使用打包工具做 tree-shaking(需要創建 webpack 化的小程序項目)。還有像是 moment 有替代方案,去掉這個也能讓小程序變小。再開啟 GZIP 壓縮。
  • 分包預下載。小程序提供的能力,可以在進入某個頁面預下載接下來可能會用到的分包,避免切頁面白屏。
  • 部分頁面 H5 化。小程序提供了 web-view 組件,可以直接引用外部 H5 頁,一方面這也能減少小程序包的體積,一方面讓經常需要高度變化的內容擴展更改的更便捷。

若是首次有效繪制 FMP 時間長,怎么優化?

骨架屏

  • 上骨架屏吧,這是縮短 FMP 首屏渲染時間的重要手段,可以減緩用戶的焦慮感。

圖片優化

  • 減少靜態資源文件的大小,使用 webp,合適的圖片壓縮工具,合理的剪裁降質來減少圖片體積
  • CDN 圖床加快圖片的獲取速度。
  • 降級加載大圖資源。對於真的需要一張渲染很大的一張圖片,可以預先使用高度壓縮模糊的圖片占位替代(給一個量化標准是 200kb以內),等到原圖加載完畢再轉移到真實節點上渲染。只需要一個 display: none 的 image 標簽,就可以做到只加載圖片資源,但不渲染。

接口拉取優化

  • 接口本地緩存;開啟數據預拉取,小程序啟動時,微信服務器可以代理小程序客戶端獲取 HTTP 請求,當小程序加載完后調用 wx.getBackgroundFetchData 從本地緩存拿數據。
  • 跳轉時預拉取數據。從上個頁面跳轉到下一個頁面,環境初始化以及頁面實例化的工作需要耗時 300~400 ms,所以可以不在 onLoad 鈎子觸發再發起網絡請求,取而代之可以在上一個頁面 wx.navigateTo 調用前獲取接口,並存儲在全局 Promise 對象中,等待下一個頁面加載完成再從 Promise 對象中讀取數據。這也是雙線程模型的優勢,不同頁面跳轉不需要銷毀全局對象。
  • 非關鍵渲染數據延遲請求。
  • 請求合並。wx.request(HTTP連接)的最大並發數量是 10 個,請求頻繁的時候可以檢查是否可以合並一段時間內的請求數據,做一個請求發送給服務器。

若是可交互時間 TTI 長,怎么優化

wx.navigateTo 跳轉時

  • 准備新的 webview 線程環境,初始化基礎庫
  • 從邏輯層到視圖層初始數據通信
  • 視圖層結合 WXML、WXSS,初始數據來渲染頁面。

切換時主要性能損耗在 數據通信節點數創建/更新

  • 降低線程通信頻次
  • 減少通信數據量
  • 減少 WXML 節點數量。
  • 減少事件數量及觸發次數

做法有

  • 合並 setData 調用
  • 只把與渲染渲染相關的數據放到 data 中。
  • 應用層數據 diff。只要調用 setData 那就必然會有視圖層渲染(當然視圖層自己也會合並 data 數據另找時間渲染),那么開發者自己可以比對兩次數據是否有變化,有變化再 setState
  • 去掉不必要的事件綁定。事件信息也需要從視圖層通信反饋給邏輯層,所以盡量減少不必要的事件綁定,尤其是 onPageScroll 里面還 setData 的這種。
  • 適當的組件顆粒度。嵌套層級不能太深,組件數量和小程序代碼包大小正相關。但也不能往一個組件上掛載太多的東西。

內存占用高?怎么優化

內存占用高,內存空間會被系統銷毀,或者被微信客戶端主動回收,導致小程序 Crash

小程序提供了監聽內存不足告警的事件 API:wx.onMemoryWarning,可以告知開發者內存緊張。但開發者無法操作內存資源,頂多調用 wx.reLaunch 來清理頁面棧,重新加載頁面,來降低內存負荷。但開發者更應該仔細取收集告警信息上報到日志系統,分析並對程序做優化。

  • 回收后台頁面計時器。從上一個頁面切換到下一個頁面,上一個頁面的定時器 setTimeout 和 setTimeInterval 仍舊會繼續執行,可能就會占用過多的內存資源,onHide 時清理掉,onShow 時再恢復。比如說小程序的 <swiper> 組件,進入后台時仍舊繼續輪播。
  • 避免頻發事件中重度內存操作。比如說導航欄吸頂效果,onPageScroll 事件回調必須使用節流函數,並在它的回調中避免使用 setData。部分場景盡量使用 IntersectionObserver API。
  • 大圖,長列表優化。快速滾動長列表,被銷毀的組件可能來不及加載完。小程序提供了長列表組件,virtual List

參考鏈接

京東京喜小程序的高性能打造之路

掘金-小程序頁面加載性能優化

如何在 H5 和小程序項目中計算白屏時間和首屏時間,說說你的思路


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM