來(lái)源:atuanxy 發(fā)布時(shí)間:2018-08-13 14:14:59 閱讀量:1128
前言
數(shù)據(jù)表明,即使在資源有緩存的情況下,首次訪問(wèn)頁(yè)面的耗時(shí)也是非首次訪問(wèn)的兩倍。為什么首次訪問(wèn)會(huì)這么耗時(shí)呢?本文詳細(xì)分析頁(yè)面首次訪問(wèn)耗時(shí)的原因。
常見(jiàn)的初始化
我們先看看打開(kāi)一個(gè)頁(yè)面,需要經(jīng)過(guò)那些流程。可能會(huì)包括,庫(kù)加載(Load so,jar文件),外殼初始化,內(nèi)核初始化,創(chuàng)建WebView,創(chuàng)建Renderer進(jìn)程,初始化V8 JS引擎,初始化IPC,初始化CC,初始化網(wǎng)絡(luò)庫(kù),初始化文件系統(tǒng),初始化數(shù)據(jù)庫(kù),啟動(dòng)ServiceWorker線程(PWA頁(yè)面),DNS解析,創(chuàng)建網(wǎng)絡(luò)連接,頁(yè)面服務(wù)器初始化,等等。這些流程前端一般是看不見(jiàn)的。
在討論具體的耗時(shí)之前,我們先約定,下文所有的數(shù)據(jù)都是基于Nexus 5手機(jī)。不同的手機(jī)的性能數(shù)據(jù)差異極大,一些高端手機(jī)(比如,iPhone X),性能可能是中低端機(jī)的好幾倍。
庫(kù)加載
啟動(dòng)應(yīng)用程序,需要先把一些庫(kù)文件加載進(jìn)內(nèi)存,這個(gè)過(guò)程是特別耗時(shí)的,一般瀏覽器內(nèi)核庫(kù)有30M,大概需要300ms。如果是全新安裝首次啟動(dòng),還涉及到解壓so和使用DexClassLoader去load dex,這些極其耗時(shí),甚至?xí)^(guò)5秒。如果是使用multidex方案,在4.x及以下系統(tǒng),會(huì)進(jìn)一步增加啟動(dòng)耗時(shí)。
外殼初始化
瀏覽器外殼的初始化,用戶(hù)點(diǎn)擊桌面圖標(biāo)啟動(dòng)瀏覽器,瀏覽器會(huì)進(jìn)入一個(gè)狀態(tài)機(jī),按步驟初始化各個(gè)功能模塊,很多模塊的初始化會(huì)涉及網(wǎng)絡(luò),文件IO,JNI,等等操作,這些都會(huì)有一定的耗時(shí)。
我們?yōu)槭裁葱枰P(guān)心瀏覽器啟動(dòng)的耗時(shí)呢?一些場(chǎng)景下,用戶(hù)通過(guò)掃碼或者點(diǎn)擊桌面圖標(biāo)去訪問(wèn)頁(yè)面,這個(gè)過(guò)程就會(huì)包含瀏覽器的啟動(dòng)流程,我們有必要了解這其中發(fā)生了什么。對(duì)于內(nèi)置瀏覽器內(nèi)核的App,比如,支付寶,手淘,情況又是怎樣的呢?我們這邊暫時(shí)沒(méi)有支付寶和手淘的啟動(dòng)性能數(shù)據(jù),但模塊初始化,加載SO和JAR,這些流程都會(huì)有,時(shí)間不會(huì)很小。
在外殼初始化耗時(shí)方面,有沒(méi)有一些比較好的解決辦法呢?最好的辦法就是進(jìn)程?;?,現(xiàn)在國(guó)內(nèi)很多手機(jī)廠商都會(huì)給微信,支付寶,等超級(jí)App的進(jìn)程?;睿脩?hù)在任務(wù)列表殺掉了應(yīng)用,其實(shí)進(jìn)程還在。如果是多進(jìn)程的情況,可以提前創(chuàng)建進(jìn)程,比如,微信和支付寶的小程序,用戶(hù)訪問(wèn)時(shí)可以直接使用預(yù)創(chuàng)建的進(jìn)程。
內(nèi)核初始化
瀏覽器內(nèi)核的初始化,與外殼的初始化類(lèi)似,內(nèi)核的初始化也需要加載SO和JAR,創(chuàng)建WebView和初始化各個(gè)功能模塊。
在創(chuàng)建WebView方面,全新安裝首次創(chuàng)建約1秒,非全新安裝首次創(chuàng)建約300ms,第二次創(chuàng)建約15ms。首次創(chuàng)建Renderrer進(jìn)程,初始化IPC,初始化CC,這些耗時(shí)在百毫秒的級(jí)別;V8 引擎相關(guān)的初始化耗時(shí)也在百毫秒的級(jí)別,其中首次NewContext要20ms。
總的來(lái)說(shuō),首次訪問(wèn)加載SO和JAR一般需要500ms,創(chuàng)建WebView和走完內(nèi)核流程一般需要消耗500ms,也就是說(shuō),應(yīng)用程序如果提前初始化內(nèi)核,預(yù)創(chuàng)建WebView和加載一個(gè)空白內(nèi)容頁(yè)面,跑一趟內(nèi)核流程,可以為用戶(hù)首次訪問(wèn)頁(yè)面帶來(lái)1秒的收益。
業(yè)務(wù)初始化
在頁(yè)面加載的過(guò)程中,內(nèi)核會(huì)有很多回調(diào)通知外殼,這些回調(diào)的處理上是否可能存在性能問(wèn)題呢?
我們發(fā)現(xiàn),在一些App上,一些接口很可能會(huì)出現(xiàn)性能問(wèn)題,比如,onPageStarted,shouldOverrideUrlLoading,shouldInterceptRequest。
這些接口為什么會(huì)出現(xiàn)性能問(wèn)題呢?一般很多應(yīng)用會(huì)在首次onPageStarted回調(diào)時(shí)執(zhí)行復(fù)雜的業(yè)務(wù)邏輯,比如,初始化一些統(tǒng)計(jì)模塊,進(jìn)行JS注入,等等。需要說(shuō)明的是,onPageStarted并不是同步接口,為什么也會(huì)有影響呢?因?yàn)樗窃赨I線程執(zhí)行的,長(zhǎng)期占用UI線程,會(huì)對(duì)內(nèi)核有較大的影響,內(nèi)核很多操作需要拋轉(zhuǎn)到UI線程去處理,比如,ServiceWorker線程啟動(dòng)就有拋轉(zhuǎn)UI的過(guò)程,在UI執(zhí)行完之前,它只能等待。
shouldOverrideUrlLoading 是客戶(hù)端攔截請(qǐng)求的關(guān)鍵接口,內(nèi)核會(huì)同步等待,很多應(yīng)用會(huì)有比較復(fù)雜的攔截規(guī)則,往往比較耗時(shí)。
shouldInterceptRequest 是客戶(hù)端離線包的關(guān)鍵接口,內(nèi)核會(huì)同步等待,很多應(yīng)用會(huì)在這個(gè)接口首次回調(diào)時(shí)去解壓離線包和初始化離線模塊。
在一些實(shí)際應(yīng)用中,優(yōu)化這些回調(diào)的處理,可以給全部H5頁(yè)面帶來(lái) 10% 以上的性能提升。
ServiceWorker初始化
ServiceWorker是PWA的關(guān)鍵技術(shù),它具有非常強(qiáng)大的能力,F(xiàn)etch,Cache,Push和Add to home screen,能讓前端開(kāi)發(fā)者非常靈活的操控頁(yè)面緩存。
同時(shí),它也是有比較大的初始化成本的,比如,ServiceWorker線程啟動(dòng)平均要200ms,而每次訪問(wèn)頁(yè)面,一般ServiceWorker線程至少都需要啟動(dòng)一次。當(dāng)然,Chrome也在不斷優(yōu)化這塊的耗時(shí),最終預(yù)計(jì)能優(yōu)化到100ms以?xún)?nèi)。
關(guān)于ServiceWorker啟動(dòng)的性能,請(qǐng)參考:PWA系列 - Service Workers 啟動(dòng)性能
網(wǎng)絡(luò)初始化
在網(wǎng)絡(luò)初始化方面,一般內(nèi)核網(wǎng)絡(luò)庫(kù)的初始化并不太耗時(shí),耗時(shí)的是DNS和Connection。
用戶(hù)首次訪問(wèn),一般都需要去進(jìn)行DNS解析和創(chuàng)建連接,而在后續(xù)訪問(wèn)時(shí),一般都可以用上緩存或者預(yù)連接。
DNS解析,一般耗時(shí)在200ms以上,創(chuàng)建HTTP連接,一般耗時(shí)也在200ms以上,而創(chuàng)建HTTPS連接則需要600ms以上。
也就是說(shuō),用戶(hù)首次訪問(wèn)時(shí),如果不能提前創(chuàng)建連接,從性能的角度來(lái)說(shuō),是非常影響的。這個(gè)方面我們的建議是,使用HTTPDNS提前解析和緩存DNS,提前創(chuàng)建連接(比如,用戶(hù)點(diǎn)擊時(shí))。瀏覽器也有這方面的優(yōu)化,比如,在加載主文檔時(shí),提前發(fā)起子資源的預(yù)連接。
服務(wù)器初始化
頁(yè)面服務(wù)器和資源服務(wù)器,是否也需要初始化呢?一般也是需要的,比如,頁(yè)面訪問(wèn)過(guò)之后,頁(yè)面服務(wù)器也會(huì)有一些緩存,用戶(hù)再次訪問(wèn)時(shí)可以直接使用緩存而無(wú)需走完整的流程,但這些緩存應(yīng)該是大部分用戶(hù)都能共享的,所以實(shí)際影響不好評(píng)估。資源服務(wù)器也一樣,比如,圖床,很多是按用戶(hù)手機(jī)屏幕和網(wǎng)絡(luò)類(lèi)型來(lái)返回不同圖片的,用戶(hù)訪問(wèn)過(guò)就會(huì)放到CDN緩存中。
暫時(shí)未有數(shù)據(jù)表明服務(wù)器初始化對(duì)頁(yè)面整體性能產(chǎn)生明顯影響。但我們有另外一份數(shù)據(jù),在一個(gè)業(yè)務(wù)中,預(yù)創(chuàng)建WebView提前加載一次模版頁(yè)面,能讓全網(wǎng)平均性能優(yōu)化100ms。其中,模版頁(yè)是304的,里面的資源都是可緩存的,也就是說(shuō),這100ms的收益并不來(lái)于緩存,而是來(lái)于某些模塊的初始化。
JS初始化
這里提到的JS初始化,并不是前面說(shuō)的JS引擎相關(guān)的初始化。JS初始化是指JS文件緩存到httpcache和解析編譯生成V8 Cache文件。很多數(shù)據(jù)表明,JS解析編譯占JS耗時(shí)的35%以上,一些有巨型JS的頁(yè)面甚至可以達(dá)到80%。在U4 2.0內(nèi)核中,一般JS執(zhí)行一次之后,就可以生成V8 Cache,雖然V8 Cache可以重復(fù)使用,但也存在被自動(dòng)清理的情況,所以提前執(zhí)行一次還是有收益的。
一些業(yè)務(wù)中,提前執(zhí)行一次JS,在用戶(hù)真實(shí)訪問(wèn)時(shí),耗時(shí)從500ms降到200ms。特別是在一些超級(jí)App中,基礎(chǔ)JS基本都一樣,提前執(zhí)行一次可能會(huì)帶來(lái)非常明顯的收益。
結(jié)束語(yǔ)
上面介紹了一些常見(jiàn)的初始化對(duì)頁(yè)面性能的影響,希望大家能了解到一些隱藏的信息,能開(kāi)闊Web優(yōu)化的思路。當(dāng)然,這些點(diǎn)不一定都會(huì)存在很大的性能問(wèn)題,比如,一些業(yè)務(wù)模塊處理的非常好的App,在業(yè)務(wù)初始化方面不一定會(huì)有性能問(wèn)題,需要根據(jù)自己的實(shí)際場(chǎng)景,具體問(wèn)題具體分析。
在線
客服
服務(wù)時(shí)間:周一至周日 08:30-18:00
選擇下列產(chǎn)品馬上在線溝通:
客服
熱線
7*24小時(shí)客服服務(wù)熱線
關(guān)注
微信
關(guān)注官方微信