企業微信端產品“C端使用者小程式”,是一款服務於vivo線下代理、門店和導購,幫助導購連線使用者,快速與使用者進行溝通的工具。基於“C端小程式”的PU/UV量較為龐大,為了更加極致的使用者體驗,所以提升小程式效能優化是必然。
一、業務現狀
1.1 存量使用者運營企業微信“使用者端小程式”
存量使用者運營企業微信“使用者端小程式”主要是服務於vivo代理、線下門店和導購,幫助導購連線使用者的工具。
- 使用者群:vivo線下使用者。
- 主要功能:vivo新機預售,新使用者註冊。
看下“使用者端小程式”的“新使用者註冊”,“新機預售”的頁面,如下:
vivo新使用者註冊
1.2 效能資料
一圖勝千言,看下存量使用者運營“使用者端小程式”在沒做優化之前,從“小程式資料助手”中獲取到的“程式碼包下載量,開啟耗時分佈,網路請求延遲,網路流量消耗”等資料。如下:
從圖中我們可以看到,下載小程式程式碼包主要集中在2-5秒,此外,部分http請求介面的時間延遲很長,會影響到整體頁面的渲染效果。由此可見,存量使用者運營“使用者端小程式”還有很大的優化空間。
二、效能指標
2.1 怎麼定義高效能?
單純的快是不行的。我們不應該單純考慮速度指標而忽略使用者的感知體驗,而應該全方位衡量使用者在使用過程中能感知到的與應用載入相關的每個節點。
2.2 效能指標關鍵術語
FCP:白屏載入結束
FMP:首屏渲染完成
TTI:所有內容載入完成
2.3 我們優化需要達到的指標
小程式官方指標:
- 首屏時間不超過 5 秒。
- 渲染時間不超過 500ms。
- 每秒呼叫 setData 的次數不超過 20 次。
- setData 的資料在 JSON.stringify 後不超過 256kb。
- 頁面 WXML 節點少於 1000 個,節點樹深度少於 30 層,子節點數不大於 60 個。
- 所有網路請求都在 1 秒內返回結果。
存量使用者運營”使用者端小程式“需要達到的指標:
- 首屏時間不超過 2.5 秒。
- setData 的資料量不超過 100kb。
- 所有網路請求都在 1 秒內返回結果。
- 元件滑動、長列表滾動無卡頓感。
三、小程式的一些基本概念
3.1 小程式底層框架
小程式的最終渲染載體依然是瀏覽器核心,而不是原生客戶端。啟用了雙執行緒模型:
- 檢視層:也就是webview執行緒,負責啟用不同的 webview 來渲染不同的小程式頁面。
- 邏輯層:一個單獨的執行緒執行 JS 程式碼,可以控制檢視層的邏輯。
3.2 小程式的啟動步驟
1. 準備執行環境。
- 在小程式啟動前,微信會先啟動雙執行緒環境,並線上程中完成小程式基礎庫的初始化和預執行。
小程式基礎庫包括 WebView 基礎庫和 AppService 基礎庫,前者注入到檢視層中,後者注入到邏輯層中,分別為所在層級提供其執行所需的基礎框架能力。
2. 下載小程式程式碼包。
3. 載入小程式程式碼包。
- 在此階段,主包內的所有頁面 JS 檔案及其依賴檔案都會被自動執行。
在頁面註冊過程中,基礎庫會呼叫頁面 JS 檔案的 Page 構造器方法,來記錄頁面的基礎資訊(包括初始資料、方法等)。
4. 初始化小程式首頁。
- 在小程式程式碼包載入完之後,基礎庫會根據啟動路徑找到首頁,根據首頁的基礎資訊初始化一個頁面例項,並把資訊傳遞給檢視層,檢視層會結合 WXML 結構、WXSS 樣式和初始資料來渲染介面。
3.3 小程式開發工具——體驗評分工具audits
(ps:小程式開發者工具的評分外掛audits可以對小程式的效能,使用體驗,實踐,UI樣式,http請求等多個維度進行綜合評分,建議小程式開發者在專案開發中使用。)
四、小程式優化技術方案
4.1 針對小程式啟動太慢的方案
方案1:無用的檔案,函式,wxss樣式剔除,不需要的import砍掉。
方案2:減少程式碼包中的靜態資原始檔。
除了部分圖片必須放在程式碼包(譬如網路異常提示)之外,建議開發者把圖片、視訊等靜態資源都放在 CDN 上。
方案3:邏輯後移,精簡業務邏輯。
儘量把業務邏輯寫在後端,如果一旦出現線上問題,小程式發版需要騰訊稽核,而後端可以及時釋出程式碼。
方案4:分包載入與分包預下載。
方案5:部分頁面h5化。
- 小程式提供了 web-view 元件,支援在小程式內訪問h5。如果小程式原始碼太大從而影響下載時間,可以考慮降級處理,把部分頁面 h5 化。
- 具體可參考web-view文件。
4.2 針對小程式白屏時間過長的方案
小程式的白屏階段:小程式程式碼包下載完(也就是啟動介面結束)之後,頁面完成首屏渲染的這一階段,也就是 FMP (首次有效繪製)。
影響白屏的兩個因素:
- 網路資源載入時間。
- 渲染時間。
方案1:啟用本地快取。
- 將請求介面中獲取到的資料儲存在storage裡面,部分資料不需要每次傳送http請求獲取。
方案2:跳轉頁面時預拉取。
- 一般是在頁面onload的時候去獲取介面資料。
- 可以在呼叫wx.navigateTo之前先呼叫下一個頁面的http介面,將資料儲存在全域性的promise裡面,下一個頁面onload的時候,直接從promise獲取資料。
(ps:在A頁面onHide或者onUnload的時候通過promise請求下一個頁面B頁面的http介面,在B頁面onload或者onShow的時候從promise中獲取資料。)
方案3:非關鍵渲染資料延遲請求。
- 將頁面分為主體模組(骨架,列表資料)和非主體模組(彈窗等)。
- 非主體模組的資料請求可以延遲載入,使用setTimeout來請求介面。
方案4:分屏渲染。
- 將非主體模組分屏渲染。
- 如下圖,我們將A模組設定為主體屏,B,C模組設定為非主體屏,等A模組渲染完成後在渲染B,C模組。
方案5:骨架屏。
- 可以使用尺寸穩定的骨架屏,來輔助實現真實模組佔位和瞬間載入。
方案6:限制http請求數量。
- wx.request (HTTP 連線)的最大併發限制是 10 個。
- wx.connectSocket (WebSocket 連線)的最大併發限制是 5 個。
方案7:圖片資源優化。
- 使用WebP格式。
- 圖片裁剪,壓縮,雪碧圖
- 圖片懶載入
4.3 提升渲染效能
概念:當呼叫 wx.navigateTo 開啟一個新頁面時,小程式框架會完成以下幾步:
- 準備新的 webview 執行緒環境,包括基礎庫的初始化。
- 從邏輯層到檢視層的初始資料通訊。
- 檢視層根據邏輯層的資料,結合 WXML 片段構建出節點樹(包括節點屬性、事件繫結等資訊),最終與 WXSS 結合完成頁面渲染。
小程式的渲染損耗主要在資料通訊和節點樹建立及更新的流程中,提升渲染效能優化的方向:
- 降低執行緒間通訊頻次。
- 減少執行緒間通訊的資料量。
- 減少 WXML 節點數量。
方案1:合併setData呼叫。
儘可能地把多次setData呼叫合併成一次。
只把與介面渲染相關的資料放在Data中。
方案2:去掉不必要的事件繫結。
不必要的click、touch、onPageScroll不要被觸發。
方案3:去掉不必要的節點屬性。
元件節點支援附加自定義資料 dataset,當使用者事件被觸發時,檢視層會把事件 target 和 dataset 資料傳輸給邏輯層。如下例:
<view class="tabbar-item" wx:for="{{list}}" wx:key="index" item="item"> <view @tap="tabFn" data-index="{{item}}"> </view> </view> methods = { tabFn (e) { const item = e.currentTarget.dataset['item']; console.log(item); } };
自定義資料量越大,事件通訊的耗時就會越長,所以儘量減少自定義資料量,或者不用自定義資料。
4.4 解決小程式記憶體佔用過高的問題
當小程式佔用系統資源過高,就有可能會被系統銷燬或被微信客戶端主動回收,導致小程式掛掉。
方案1:回收頁面的setTimeout和setInterval計時器。
小程式每一個頁面都會獨立一個 webview 執行緒,但邏輯層是單執行緒的,也就是所有的 webview 執行緒共享一個 JS 執行緒,以至於跳轉頁面,計時器還在跑。
onHide或者onUnload的時候清除計時器。
方案2:避免頻發事件中的重度記憶體操作。
- onPageScroll 事件回撥使用節流。
- 避免 CPU 密集型操作,譬如複雜的計算。
- 避免呼叫 setData,或減小 setData 的資料量。
五、優化後的結果
5.1 看下audis下的得分
優化之前得分如下:
優化之後得分如下:
由此可見,按照以上步驟做小程式效能優化之後,audis的綜合評分是有一個明顯的進步的。
5.2 優化之後,“小程式資料助手”中的效能資料
六、總結
小程式效能優化和H5優化一樣,是一個根據多樣性使用者場景做持續迭代的過程,也是我們日常做web開發揮之不去的原則和主題。本文探討了小程式優化的各種場景和方案,希望在以後的專案開發過程中,能夠持續優化,打造出更好的產品。
作者:vivo-Fu Weilang