網頁的性能優化是前端開發老生常談的熱門話題,其中微信小程序因其頁面雙線程架構設計,所以性能優化的手段跟傳統的 H5
應用不太一樣。今天主要介紹一下微信小程序頁面雙線程架構的特性給頁面渲染帶來的一些影響,以及應對的一些渲染性能調優策略。為了敘述方便,下文會把微信小程序簡稱為小程序。
小程序的雙線程架構
與傳統的瀏覽器Web頁面最大區別在于,小程序的是基于 雙線程
模型的,在這種架構中,小程序的渲染層使用 WebView
作為渲染載體,而邏輯層則由獨立的 JsCore
線程運行 JS
腳本,雙方并不具備數據直接共享的通道,因此渲染層和邏輯層的通信要由 Native
的 JSBrigde
做中轉。
小程序更新視圖數據的通信流程
每當小程序視圖數據需要更新時,邏輯層會調用小程序宿主環境提供的 setData
方法將數據從邏輯層傳遞到視圖層,經過一系列渲染步驟之后完成UI視圖更新。完整的通信流程如下:
引發渲染性能問題的一些原因
在上述通信流程中,一些不恰當的操作可能會影響到頁面渲染的性能:
setData傳遞大量的新數據
數據的傳輸會經歷跨線程傳輸和腳本編譯的過程,當數據量過大,會增加腳本編譯的執行時間,占用 WebView JS
線程。
下圖是我們做的一組測試統計:在相同網絡環境下,各個機型分別對大小為 1KB
、 2KB
、 3KB
的數據執行 setData
操作所消耗的時間。
從圖中可以看出, setData
數據傳輸量越大,數據傳輸所消耗的時間越大。
頻繁的執行setData操作
頻繁的執行 setData
會讓 WebView JS
線程一直忙碌于腳本的編譯、節點樹的對比計算和頁面渲染。導致的結果是:
過多的頁面節點數
setData
更新視圖, WebView JS
線程都要遍歷節點樹計算新舊節點數差異部分。當頁面節點數量越多,計算的時間開銷越大,減少節點樹節點數量可以有效降低重渲染的時間開銷。渲染性能優化
基于引發渲染性能問題的原因,我們可以制定一些優化策略來避免性能問題的發生。
setData優化
setData
作為邏輯層與視圖層通信的媒介,是最容易造成渲染性能瓶頸的 API
。我們在使用 setData
時應該遵循一些規則來盡可能避免性能問題的發生:
減少 setData
數據傳輸量
JS
環境用到的數據存放到 data
對象外。setData
是支持使用 數據路徑
的方式對對象的局部字段進行更新,我們可能會遇到這樣的場景: list
列表是從后臺獲取的數據,并展示在頁面上,當 list
列表的第一項數據的 src
字段需要更新時,一般情況下我們會從后臺獲取新的 list
列表,執行 setData
更新整個 list
列表。// 后臺獲取列表數據 const list = requestSync(); // 更新整個列表 this.setData({ list });
實際上,只有個別字段需要更新時,我們可以這么寫來避免整個 list
列表更新:
// 后臺獲取列表數據 const list = requestSync(); // 局部更新列表 this.setData({ 'list[0].src': list[0].src });
降低 setData
執行頻率
setData
調用合并執行,減少線程間通信頻次。PageScroll
、 Resize
事件)中調用 setData
,合理的利用 函數防抖(debounce)
和 函數節流(throttle)
可以減少 setData
執行次數。 函數防抖(debounce)
:函數在觸發n秒后才執行一次,如果在n秒內重復觸發函數,則重新計算時間。
函數節流(throttle)
:單位時間內,只會觸發一次函數,如果同一個單位時間內觸發多次函數,只會有一次生效。
除了讓開發者自覺遵循規則來減少 setData
數據傳輸量和執行頻率之外,我們還可以自己設計一個 diff
算法,重新對 setData
進行封裝,使得在 setData
執行之前,讓待更新的數據與原 data
數據做 diff
對比,計算出數據差異 patch
對象,判斷 patch
對象是否為空,如果為空則跳過執行更新,否則再將 patch
對象執行 setData
操作,從而達到減少數據傳輸量和降低執行 setData
頻率的目的。
// setData重新封裝成新的方法,使得數據更新前先對新舊數據做diff對比,再執行setData方法this.update = (data) => { return new Promise((resolve, reject) => { const result = diff(data, this.data); if (!Object.keys(result).length) { resolve(null); return; } this.setData(result, () => { resolve(result); }); });}
當然,可以直接引用這里 的現成 高性能小程序 setData
diff算法
具體流程如下圖:
善用自定義組件
小程序自定義組件的實現是由小程序官方設計的 Exparser
框架所支持,框架實現的自定義組件的組件模型與 Web Components
標準的 Shadow DOM
相似:
在頁面引用自定義組件后,當初始化頁面時, Exparser
會在創建頁面實例的同時,也會根據自定義組件的注冊信息進行組件實例化,然后根據組件自帶的 data
數據和組件WXML,構造出獨立的 Shadow Tree
,并追加到頁面 Composed Tree
。創建出來的 Shadow Tree
擁有著自己獨立的邏輯空間、數據、樣式環境及setData調用:
基于自定義組件的 Shadow DOM
模型設計,我們可以將頁面中一些需要高頻執行 setData
更新的功能模塊(如倒計時、進度條等)封裝成自定義組件嵌入到頁面中。當這些自定義組件視圖需要更新時,執行的是組件自己的 setData
,新舊節點樹的對比計算和渲染樹的更新都只限于組件內有限的節點數量,有效降低渲染時間開銷。
下圖是我們在微保小程序WeDrive首頁中,將倒計時模塊抽取自定義組件前后的setData更新耗時對比:
從圖中可以看出,使用自定義組件后,倒計時模塊 setData
平均渲染耗時有了非常明顯的下降,實際在低端安卓機中體驗會感覺明顯的更流暢。
當然,并不是使用自定義組件越多會越好,頁面每新增一個自定義組件, Exparser
需要多管理一個組件實例,內存消耗會更大,當內存占用上升到一定程度,有可能導致 iOS
將部分 WKWebView
回收,安卓機體驗會變得更加卡頓。因此要合理的使用自定義組件,同時頁面設計也要注意不濫用標簽。
總結
小程序雙線程架構決定了數據通信優化會是性能優化中一個比較重要的點。而上述提到的幾個優化建議只是優化渲染性能中的一部分,要想讓你的頁面體驗變得更加絲滑,就要熟悉小程序框架的底層原理,根據小程序框架的特點,編寫出“合身”的前端代碼。
參考資料
微信小程序官方開發指南
小程序高性能setData diff算法
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。
|
新聞熱點
疑難解答