企業微信端產品“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