首屏,白屏時間如何計算??


做移動web頁面,受移動網絡網速和終端性能影響,我們經常要關注首屏內容展示時間(以下簡稱首屏時間)這個指標,它衡量着我們的頁面是否能在用戶耐心消磨完之前展示出來,很大程度影響着用戶的使用滿意度。

怎么獲取首屏時間呢?

我們經常要先問自己:頁面是怎么加載數據?

A:加載完靜態資源后通過ajax請求去后台獲取數據,數據回來后渲染內容

QQ截圖20160109164815

 

在每個點打上一個時間戳,首屏時間 = 點8 – 點1;

B:使用后台直出,返回的html已經帶上內容了

QQ截圖20160109165343

此時首屏時間 = 點4 – 點1。

注:1. 打了這么多個點,是因為當我們收集到首屏時間之后,要去分析到底是哪一段是性能瓶頸,哪一段還有優化空間,所以我們需要收集 點2 – 點1、點3 – 點1 ……這些時間以作分析;

2. 打點1我們一般是在html文件head標簽的開頭打個時間戳;

3. 在css文件加載前一般沒有別的加載處理,所以打點1和打點2一般可以合並。

 

到此我們就收集到首屏相關各種數據,可以做各種針對性優化。Wait!在你大刀闊斧優化前,你要了解一些細節,它們有利於你做更准確的分析和更細致的優化。

細節1:js后面的點 – js前面的點 ≠ js的加載時間

9171cc76404a83c387079c40f6517a3c913dc55d

JsEndTime – JsStartTime = js文件的加載時間,對嗎?

不對!明顯地,這個等式忽略了js的執行時間。js執行代碼是需要花費時間的,特別是做一些復雜的計算或頻繁的dom操作,這個執行時間有時會達到幾百毫秒。

那么,JsEndTime – JsStartTime = js文件的加載執行時間?

依然不對!因為CSS文件的加載執行帶來了干擾。覺得很奇怪對吧,別急,我們來做個試驗:我們找一個demo頁面,在chrome里面打開,然后啟動控制台,模擬低網速,讓文件加載時間比較久:QQ截圖20160109180011

先在正常情況下收集 JsEndTime – JsStartTime 的時間,然后使用fiddler阻塞某一條css請求幾秒鍾:

QQ截圖20160109180537

然后再恢復請求,拿到此時的 JsEndTime – JsStartTime 結果,會發現第一次的時間是幾百毫秒將近1s,而第二次的時間低於100ms甚至接近為0(我的示例,時間視讀者具體的js文件決定),兩者的差距非常明顯。

這是什么原理?這就是我們常說的”加載是並行的,執行是串行的“的結果。html開始加載的時候,瀏覽器會將頁面外聯的css文件和js文件並行加載,如果一個文件還沒回來,它后面的代碼是不會執行的。剛剛我們的demo,我們阻塞了css文件幾秒,此時js文件因為並行已經加載回來,但由於css文件阻塞住,所以后面 JsStartTime 的賦值語句是不執行的!當我們放開阻塞,此時才會運行到 JsStartTime 的賦值、js文件的解析、JsEndTime的賦值,由於大頭時間加載早已完成,所以 JsEndTime 和 JsStartTime 的差值非常小。

 

知道這個有何用?

  1. 別再把 JsEndTime – JsStartTime 的結果成為js文件的加載執行時間(除非你沒有外聯css文件),不然會被內行人取笑滴;
  2. css文件的阻塞會影響后面js代碼的執行,自然也包括html代碼的執行,即是說此時你的頁面就是空白的。所以css文件盡量內聯,你可以讓構建工具幫你忙;
  3. 如果真想要知道js文件的加載時間,最正確的姿勢是使用 Resource Timing API,不過這個API移動端只能在Android4.4及以上的版本拿到數據,也就在業務PV大的場景才夠我們做分析用

當然,那兩個打點留着還是可以做分析用的。

 

細節2:html里面外聯的js文件,前一個文件的加載會阻塞下一個文件的執行;而如果a.js負責渲染並會動態拉取js、拉取cgi並做渲染,會發現它后面的js文件再怎么阻塞也不會影響到它的處理

前半部分的結論在細節1里面已經證明,因為瀏覽器的執行是串行的。這說明,我們負責渲染內容的js代碼要等到它前面所有的js文件加載執行完才會執行,即使那些代碼跟渲染無關的代碼如數據上報:

QQ截圖20160109191818

而后半部分的結論很好驗證,我們在負責渲染的js文件后面外聯一個別的js文件並把它阻塞住,你會發現渲染相關的js不管是動態拉取新的js文件、拉取渲染相關內容都一切正常,頁面內容順利渲染出來,它們的執行並不需要等被阻塞的這個文件。

 

知道這個有何用?

  1. 無關緊要”的js不要放在負責渲染的js前面,這里的“無關緊要”是指和首屏渲染無關,如數據上報組件。我們可以選擇將要上報的數據臨時存起來,先繼續執行渲染的js,等負責渲染的js執行完再加載上報組件再上報。甚至連zepto之類的庫我們也可以放后面,把渲染相關的代碼抽離出來並用原生js書寫,放到最前面;
  2. 可以看到,動態加載的js的執行是不會受到html后面外聯的js的阻塞的影響,即是說,它的執行和后面js的執行順序是不確定的。因此我們要小心處理好文件的依賴關系。當然還可以采用最不容易出錯的方法:負責動態加載js的文件是html里面外聯的最后一個文件

(注:個人覺得這是全文最重要的兩點結論,因為我正在做首屏優化^-^)

 

細節3:如果html的返回頭包含chunk,則它是邊返回邊解析的,不然就是一次性返回再解析。這個是在服務器配置的

QQ截圖20160109165343

打點1一般寫在html里head標簽的最前面,時常有朋友拿直出時的 點4 – 點1 的時間和非直出時 點8 – 點1 的時候做對比,來說明直出優化了多少多少毫秒,我倒覺得不一定。要知道直出的情況html文件包含渲染后的內容和dom節點,文件大小一般比非直出大,有時甚至大個幾十K都有,那我覺得要說明直出優化了多少就要把html的加載時間考慮進去了。那上面的計算方法是否考慮上html的加載時間?

那就要看html文件的返回頭是否包含chunk:

1

如果包含這個返回頭,那html文件是邊返回邊解析的,此時上面的計算方法是合理的。如果不包含這個頭,則html文件是整一個返回來后才開始解析,此時上面的計算方法就少算了html的加載時間,也就不夠精准。這個返回頭是由后台控制的。

 

知道這個有何用?

  1. 如果我們想說明直出的優化程度,最好先瞧瞧你的html返回頭。如果不包含chunk返回頭,考慮拿HTML5 performance里面的 navigationStart 作為打點1(這個API也是Android4.4及以上才支持),要不就要評估文件大小變化做點修正了;
  2. 對於沒有啟用chunk的html,建議不要inline太多跟渲染首屏內容無關的js在里面,這樣會影響渲染時間

 

細節4:寫在html里面的script節點的加載和解析會影響 domContentLoaded 事件的觸發時間

我們有時會用 domContentLoaded 事件代替 onload 事件,在頁面准備好的時候做一些處理。然而要知道,domContentLoaded里面的dom不止包含我們常說的普通dom節點,還包括script節點。

試驗一下,我們將頁面里面外聯的一個js文件阻塞住一段時間再放開,我們看下chrome控制台:

 

很明顯,js文件的加載時間會影響這個事件的觸發事件。那js代碼的解析時間會不會影響?我們在最后一個外聯js文件后面打了一個點,它的時間是:

3

所以js文件加載執行會影響domContentLoaded事件的執行時機。

知道這個有何用?

  1. 如果我們打算在domContentLoaded、onLoad 事件里面做一些特殊處理且這些處理比較重要(如跟渲染有關),那我們最好就不要在html里面直接外聯一些跟渲染無關的js文件,可以考慮改用動態加載

總結

研究首屏時間和資源加載是一件挺有意思的事情,大家利用好chrome控制台(特別是里面的network標簽)以及fiddler可以挖掘出很多有趣的小細節小結論。別以為這是在沒事找事,理解好這些對大家做首屏性能優化、定位因為js文件執行順序錯亂導致報錯等場景是非常有好處的。所以發現什么記得與我共享哈~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM