android工程師學習微信小程序筆記⑫ 小程序性能優化

小程序官方文檔

當開發完一個小程序之後,性能優化就是一項長期的工作,那麼我們就得了解一下如何做性能優化。

啓動

在小程序啓動時,微信會爲小程序展示一個固定的啓動界面,界面內包含小程序的圖標、名稱和加載提示圖標。

此時,微信會在背後完成幾項工作:

  • 下載小程序代碼包
  • 加載小程序代碼包
  • 初始化小程序首頁。

在這裏插入圖片描述

代碼包下載

在某個小程序第一次啓動時,微信需要下載小程序代碼包。此後,如果小程序代碼包未更新且還被保留在緩存中,則下載小程序代碼包的步驟會被跳過。下載到的小程序代碼包不是小程序的源代碼,而是編譯、壓縮、打包之後的代碼包。代碼包大小可以在開發者工具的“詳情”欄中找到。

從開發者的角度看,控制代碼包大小有助於減少小程序的啓動時間

  • 精簡代碼,去掉不必要的WXML結構和未使用的WXSS定義。
  • 減少在代碼包中直接嵌入的資源文件。
  • 壓縮圖片,使用適當的圖片格式。

分包加載流程

如果小程序比較複雜,優化後的代碼總量可能仍然比較大,此時可以採用分包加載的方式進行優化。

一般情況下,小程序的代碼將打包在一起,在小程序啓動時一次性下載完成。採用分包時,小程序的代碼包可以被劃分爲幾個:一個是“主包”,包含小程序啓動時會馬上打開的頁面代碼和相關資源;其餘是“分包”,包含其餘的代碼和資源。這樣,小程序啓動時,只需要先將主包下載完成,就可以立刻啓動小程序。這樣就可以顯著降低小程序代碼包的下載時間。

代碼包加載

微信會在小程序啓動前爲小程序準備好通用的運行環境。這個運行環境包括幾個供小程序使用的線程,並在其中完成小程序基礎庫的初始化,預先執行通用邏輯,儘可能做好小程序的啓動準備。這樣可以顯著減少小程序的啓動時間。

小程序的代碼包被下載(或從緩存中讀取)完成後,小程序的代碼會被加載到適當的線程中執行。

此時,所有app.js、頁面所在的JS文件和所有其他被require的JS文件會被自動執行一次,小程序基礎庫會完成所有頁面的註冊。

在小程序代碼調用Page構造器的時候,小程序基礎庫會記錄頁面的基礎信息,如初始數據(data)、方法等。

需要注意的是,如果一個頁面被多次創建,並不會使得這個頁面所在的JS文件被執行多次,而僅僅是根據初始數據多生成了一個頁面實例(this),在頁面JS文件中直接定義的變量,在所有這個頁面的實例間是共享的。

頁面層級準備

在視圖層內,小程序的每一個頁面都獨立運行在一個頁面層級上。小程序啓動時僅有一個頁面層級,每次調用wx.navigateTo,都會創建一個新的頁面層級;相對地,wx.navigateBack會銷燬一個頁面層級。

對於每一個新的頁面層級,視圖層都需要進行一些額外的準備工作。在小程序啓動前,微信會提前準備好一個頁面層級用於展示小程序的首頁。除此以外,每當一個頁面層級被用於渲染頁面,微信都會提前開始準備一個新的頁面層級,使得每次調用wx.navigateTo都能夠儘快展示一個新的頁面。

頁面層級的準備工作分爲三個階段。第一階段是啓動一個WebView,在iOS和Android系統上,操作系統啓動WebView都需要一小段時間。第二階段是在WebView中初始化基礎庫,此時還會進行一些基礎庫內部優化,以提升頁面渲染性能。第三階段是注入小程序WXML結構和WXSS樣式,使小程序能在接收到頁面初始數據之後馬上開始渲染頁面(這一階段無法在小程序啓動前執行)。

在這裏插入圖片描述
對於wx.redirectTo,這個調用不會打開一個新的頁面層級,而是將當前頁面層級重新初始化:重新傳入頁面的初始數據、路徑等,視圖層清空當前頁面層級的渲染結果然後重新渲染頁面。

數據通信

在每個小程序頁面的生命週期中,存在着若干次頁面數據通信。邏輯層向視圖層發送頁面數據(data和setData的內容),視圖層向邏輯層反饋用戶事件。

頁面初始數據通信

在小程序啓動或一個新的頁面被打開時,頁面的初始數據(data)和路徑等相關信息會從邏輯層發送給視圖層,用於視圖層的初始渲染。

Native層會將這些數據直接傳遞給視圖層,同時向用戶展示一個新的頁面層級,視圖層在這個頁面層級上進行界面繪製。

視圖層接收到相關數據後,根據頁面路徑來選擇合適的WXML結構,WXML結構與初始數據相結合,得到頁面的第一次渲染結果。

在這裏插入圖片描述
分析這個流程不難得知:頁面初始化的時間大致由頁面初始數據通信時間和初始渲染時間兩部分構成。其中,數據通信的時間指數據從邏輯層開始組織數據到視圖層完全接收完畢的時間,數據量小於64KB時總時長可以控制在30ms內。傳輸時間與數據量大體上呈現正相關關係,傳輸過大的數據將使這一時間顯著增加。因而減少傳輸數據量是降低數據傳輸時間的有效方式。

在這裏插入圖片描述

更新數據通信

初始渲染完畢後,視圖層可以在開發者調用setData後執行界面更新。

在數據傳輸時,邏輯層會執行一次JSON.stringify來去除掉setData數據中不可傳輸的部分,之後將數據發送給視圖層。同時,邏輯層還會將setData所設置的數據字段與data合併,使開發者可以用this.data讀取到變更後的數據

因此,爲了提升數據更新的性能,開發者在執行setData調用時,最好遵循以下原則:

  • 不要過於頻繁調用setData,應考慮將多次setData合併成一次setData調用;
  • 數據通信的性能與數據量正相關,因而如果有一些數據字段不在界面中展示且數據結構比較複雜或包含長字符串,則不應使用setData來設置這些數據;
  • 與界面渲染無關的數據最好不要設置在data中,可以考慮設置在page對象的其他字段下。

用戶事件通信

視圖層會接受用戶事件,如點擊事件、觸摸事件等。用戶事件的通信比較簡單:當一個用戶事件被觸發且有相關的事件監聽器需要被觸發時,視圖層會將信息反饋給邏輯層。如果一個事件沒有綁定事件回調函數,則這個事件不會被反饋給邏輯層。視圖層中有一套高效的事件處理體系,可以快速完成事件生成、冒泡、捕獲等過程。

視圖層將事件反饋給邏輯層時,同樣需要一個通信過程,通信的方向是從視圖層到邏輯層。因爲這個通信過程是異步的,會產生一定的延遲,延遲時間同樣與傳輸的數據量正相關,數據量小於64KB時在30ms內。降低延遲時間的方法主要有兩個。

  • 去掉不必要的事件綁定(WXML中的bind和catch),從而減少通信的數據量和次數;
  • 事件綁定時需要傳輸target和currentTarget的dataset,因而不要在節點的data前綴屬性中放置過大的數據。

在這裏插入圖片描述

視圖層渲染

視圖層在接收到初始數據(data)和更新數據(setData數據)時,需要進行視圖層渲染。在一個頁面的生命週期中,視圖層會收到一份初始數據和多份更新數據。收到初始數據時需要執行初始渲染,每次收到更新數據時需要執行重渲染。

初始渲染

初始渲染髮生在頁面剛剛創建時。初始渲染時,將初始數據套用在對應的WXML片段上生成節點樹。節點樹也就是在開發者工具WXML面板中看到的頁面樹結構,它包含頁面內所有組件節點的名稱、屬性值和事件回調函數等信息。最後根據節點樹包含的各個節點,在界面上依次創建出各個組件。

在這裏插入圖片描述
在這整個流程中,時間開銷大體上與節點樹中節點的總量成正比例關係。因而減少WXML中節點的數量可以有效降低初始渲染和重渲染的時間開銷,提升渲染性能

重渲染

初始渲染完畢後,視圖層可以多次應用setData的數據。每次應用setData數據時,都會執行重渲染來更新界面。

初始渲染中得到的data和當前節點樹會保留下來用於重渲染

每次重渲染時,將data和setData數據套用在WXML片段上,得到一個新節點樹。然後將新節點樹與當前節點樹進行比較,這樣可以得到哪些節點的哪些屬性需要更新、哪些節點需要添加或移除。最後,將setData數據合併到data中,並用新節點樹替換舊節點樹,用於下一次重渲染。

在這裏插入圖片描述
在進行當前節點樹與新節點樹的比較時,會着重比較setData數據影響到的節點屬性。因而,去掉不必要設置的數據、減少setData的數據量也有助於提升這一個步驟的性能。

原生組件通信

一些原生組件支持使用context來更新組件。不同於setData,使用context來更新組件並不會涉及到重渲染過程,數據通信過程也不同。在setData的數據通信流程中,數據從邏輯層經過native層轉發,傳入視圖層的WebView,再經過一系列渲染步驟之後傳入組件。而使用context時,數據從邏輯層傳到native層後,直接傳入組件中,這樣可以顯著降低傳輸延遲。

在這裏插入圖片描述
在具體通信過程上,因爲context的方法繁多,通信方式相對於setData更復雜。不過基礎庫會對context方法調用時的通信進行封裝優化,通常開發者不需要關心這個問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章