在開發小程序應用中,QA發現過幾次頁面白屏的情況,苦于難易復現和調試,故想對小程序白屏問題進行一番探究。
從小程序官方開發者文檔得知,微信小程序運行在三端:iOS(iPhone/iPad)、Android和用于調試的開發者工具。三端的腳本執行環境以及用于渲染非原生組件的環境是各不相同的[1]:
下面說說WKWebView、Mobile Chrome 53/57、Mobile Chrome 53是什么。
在Apple公司的開發者文檔網站上,有對WKWebView進行介紹,簡單來說,WKWebView是一個為app內置瀏覽器渲染交互式網頁內容的組件,用于替換老版本的UIWebView組件[2]。不管是UIWebView,還是WKWebView,它們都屬于IOS WebView。我們可以把WebView理解為手機操作系統的一個系統級的組件。不管是手機內置的瀏覽器,還是其他app,比如微信等,只要你想呈現交互式的網頁內容,都可以調用WebView去完成這件事情。Android WebView亦是如此[3]。
現在我們可以把WKWebView稱為IOS端的WebView,那么Android端的Mobile Chrome 53/57,或者Mobile Chrome 53又是什么,這兩個跟WebView又是什么關系呢? 我們可以把Mobile Chrome 53/57理解為Chrome for Android 537版本,這里的537是指Chrome的排版引擎(layout engine)采用的WebKit內核版本,具體參考Google Chrome version history[4]。需要指出的是,53/57是不是就是537,這里存疑,沒有查到有效的參考資料,但是這個對我們的研究應該沒有什么影響,可以不予考慮。到這里,又引入了兩個概念:layout engine、WebKit內核。接下來簡單介紹一下layout engine和WebKit內核。
我們都知道瀏覽器有兩個重要的引擎:渲染引擎(rendering engine,也稱layout engine,即上面提到的排版引擎,后續為了方便,統一描述為渲染引擎)和JS引擎。其中渲染引擎負責解析網頁內容,計算顯示方式,輸出至顯示設備。JS引擎則負責解析JavaScript語言,實現網頁的動態交互效果。最開始時渲染引擎和JS引擎并沒有區分的很明確,后來JS引擎越來越獨立,內核就傾向于只指渲染引擎,即瀏覽器內核就是該瀏覽器采用的渲染引擎,主要參考X5內核調研報告[5]。在后續的討論中,瀏覽器內核就單指渲染引擎。
那WebKit內核又是什么?這個不得不追溯WebKit的歷史了。1998,自由軟件社區KDE開發了HTML排版引擎KHTML和JavaScript解析引擎KJS,也就是現代瀏覽器兩個重要的引擎。Apple公司的開發者Don Melton于2001年在KDE的基礎之上開始了WebKit項目。剛開始時,WebKit僅為KDE的復刻,我們可以理解為WebKit是KDE基礎上fork出來的分支。后來,在WebKit項目中,KHTML被命名為WebCore,KJS被命名為JavaScriptCore,主要參考維基百科[6]。至此,我們可以回答,至少針對Apple的產品來說,瀏覽器內核就是WebKit,即渲染引擎采用的是WebKit內核。
webkit項目是Apple公司發展自家瀏覽器啟動的項目。Google公司在發展Chrome瀏覽器也成立了Chromium項目。在Chromium項目中,JavaScript解析引擎采用Google自己開發的大名鼎鼎的V8引擎,渲染引擎采用的是WebKit內核。到2013年7月份,Chromium項目將渲染引擎替換為Blink引擎,并在Chrome28及后續的版本上采用[4][7]。Blink引擎是Google在WebKit項目中的WebCore基礎上fork出來的一個分支[8][9]。我們可以用一幅圖把KDE、WebKit和Chromium串聯起來:
現在,我們再回過頭來看一下Mobile Chrome 53/57,或者Mobile Chrome 53,其實它的內核還是從WebKit上演化而來。繞了這么遠,只為一句話:小程序就是運行在WebView之上。那么我們的初衷,研究小程序白屏問題,其實就是在探究WebView白屏問題。如果要更詳細一點,那就是WKWebview、Android WebView白屏的原因。
關于WKWebview白屏,網上羅列的常見原因大致有以下幾種:
針對原因3,解決的方案是判斷IOS系統版本,小于8.2的使用UIWebView。如果站在小程序開發者的角度,這個跟我們好像沒有關系。小程序是個平臺,我們在這個平臺上開發我們的小程序應用,如果小程序也有這個問題,那只能由小程序團隊去解決這件事情。還有,比如原因4,我們該嵌套還是得嵌套,有問題也是小程序團隊去解決。至于原因2,如果是小程序原生開發的話,頁面間的跳轉URL包含中文也是能正常跳轉的,這個應該是小程序內部兼容了。但是原因1,這個跟我們就有很大的關系了,比如我們定義了大量的變量,使用完了卻沒有釋放,那么這部分內存在小程序銷毀之前會被一直占用。再比如我們在某一刻操作了某個比較大的變量,可能在短時間內,內存使用量也會飆升。同樣的,對于導致Android WebView白屏的問題,絕大部分也只能由小程序團隊去解決。
這樣一來,從開發小程序應用的前端角度來說,我們能夠把握的是盡量避免由于內存使用緊張導致的部分WebView被回收而出現的白屏問題。至此,我們研究的小程序白屏問題,可以轉向對小程序內存優化的研究。
下面總結一下平時開發過程中可能會導致內存警告的操作:
使用大圖片和長列表圖片。根據小程序團隊分析過的大部分案例,大圖片和長列表圖片的使用,都會引起WKWebview被回收[10]。其中長列表頁圖片是指頁面包含數目較大的列表,每個列表里面又引用了圖片。
隨意定義變量,由于小程序的機制而又沒有得到釋放。以下四種場景下定義的變量,即使離開當前頁面,變量也不會被回收:
定義在Page構造器外層的全局變量。
定義在data內部的數據。
定義在Page內部,類data數據。
掛載到getApp().globalData上的數據。
假如我們在testvar頁面定義了上述變量,由testvar通過navigateTo跳轉到下一個頁面otherpage,在頁面otherpage里面我們可以通過getCurrentPages()獲取頁面testvar的引用,進而獲取里面的變量。通過navigateTo打開新頁面,上一個頁面進入頁面棧,并且該頁面只是hide,并不是unload[11]。小程序框架的頁面棧最多可支持10層頁面。設想一下,那些具有復雜交互的頁面,每層頁面都附帶了眾多的數據,甚至包含很多圖片,再考慮多層頁面并存的問題,那內存使用量將是很可觀的。在頁面棧里面的頁面unload之前,都會造成持續的內存占用。
短時間內大數據操作。假設在某個時間點,我們需要對接口返回的大量數據進行操作,可能會造成瞬時的內存占用。
列表數據的持續累加,導致某個數據異常大。設想一下,假如我們的列表頁有很多條數據,每經過一次分頁請求,我們就把新的數據concat到已有的數據之上,久而久之,這條數據可能會變成巨無霸,逐漸侵蝕我們的內存。
所幸的是,上述這些可能造成內存大量占用的操作,我們是可以避免或者優化的。
希望大家進行批評和指正!