First paint 直譯過來的意思就是瀏覽器第一次渲染(paint),在First paint以前是白屏,在這個時間點以後用戶就能看到(部分)頁面內容。css
因此研究這個First Paint的觸發時機對於優化瀏覽器頁面的首屏渲染時間有很重要的做用。html
在正題開始以前,先說下瀏覽器的頁面的加載流程(大致過程是這樣,並不精確,只是爲了幫助理解後面內容):vue
Send Request
:表示給這個外鏈對應的服務器發送請求Receive Response
: 表示接收響應,這裏是表示告訴瀏覽器能夠開始從網絡接收數據了Receive Data
:表示開始接收數據Finish Loading
: 表示已經完成下載數據。Parse Stylesheet/Evaluate
(默認狀況下js下載完成以後執行Evaluate
,css下載完成後會進行Parse Stylesheet
)Parse Stylesheet
而後開始構建CSSOMRender Tree
)Layout
,能夠理解爲「刻章」)Paint
,能夠理解爲「蓋章」)。在最新版的Chrome的perfomance
中是能直接看到First Paint這個時間點的,爲了方便你們測試,我就直接拿谷歌這個示例頁面來作演示:react
測試頁面jquery
用chrome打開上面連接,最好是隱身模式,防止插件亂入影響判斷,按F12或者右鍵檢查元素打開控制檯先切換到Network
選項,勾選禁用緩存(緩存也會影響到判斷):git
切換到Perfomance
,勾選Screenshots
並點擊紅框進行頁面分析(會自動中止的,不用點stop):github
分析完後能夠看到以下結果:web
上圖中的綠色的線就是當前頁面第一次出現內容的時間點,能夠將鼠標放到Main
上面的Network
中綠色的線附近能夠看到在他以前頁面空白,在他以後就有內容。
除了綠色的線還有藍色以及紅色的線,這裏也解釋一下:chrome
簡單講一下DOMContentLoaded
、load
的區別:bootstrap
DOMContentLoaded
是HTML文檔(包括CSS、JS)被加載以及解析完成以後觸發(即 HTML->DOM
的過程完成 )load
則是在頁面的其餘資源如圖片、字體、音頻、視頻加載完成以後觸發load
事件通常在DOMContentLoaded
以後才觸發(也有可能在它以前哦)這個時候發現綠色虛線以前有一個淺綠色方塊,相應的解釋以下:
由圖能夠得出「淺綠色」表明的是根據CSSOM計算樣式並進行佈局繪製的過程,這段時間內瀏覽器作了一下事情:
Recalculate Style
:從新計算樣式,肯定DOM元素的樣式規則(定規則)Layout
:根據計算結果進行佈局,肯定元素的大小和位置(刻章)Update Layer Tree
: 更新渲染層樹Paint
: 繪製,根據前面的Layer Tree繪製頁面(位置、大小、顏色、邊框、陰影等)(蓋章)Composite Layers
: 造成層,瀏覽器按照合理的順序合併成一個圖層而後輸出到屏幕(給別人看)那何時開始First paint
呢?在淺綠色方塊最前面的虛線往前看,發如今灰色虛線以前都會有一個步驟:就是Parse Stylesheet
(調研了不少頁面都是如此)
因此,First Paint的加載流程應該是這樣:
Parse Stylesheet
:構建出CSSOMRecalculate Style
:從新計算樣式,肯定DOM元素的樣式規則(定規則)Layout
:根據計算結果進行佈局,肯定元素的大小和位置(刻章)Update Layer Tree
:更新渲染層樹Paint
:繪製,根據前面的Layer Tree繪製頁面(位置、大小、顏色、邊框、陰影等)(蓋章)Composite Layers
:造成層,瀏覽器按照合理的順序合併成一個圖層而後輸出到屏幕(給別人看)可是如今還只是肯定了First Paint
的加載流程,也肯定了他是在全部CSS執行完Parse Stylesheet
以後纔會觸發,可是這仍是不夠準確啊,因此我找了一些CSS和JS的外鏈來測試,模板以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.css" rel="stylesheet"> <link href="https://cdn.bootcss.com/jqueryui/1.12.1/jquery-ui.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/vue/2.5.13/vue.js"></script> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script src="https://cdn.bootcss.com/react/16.4.0-alpha.0911da3/cjs/react.development.js"></script> <script src="https://cdn.bootcss.com/angular.js/2.0.0-beta.17/angular2.js"></script> </head> <body> <div id='root1'> 1 </div> <div id='root2'> 2 </div> <div id='root3'> 3 </div> </body> </html>
咱們經過改變上面模板裏的外鏈順序來探究:
發現FP發生在最後(實心的藍色線是按shift
出來的,不是DOMContentLoaded
),如今還發現不了什麼。
調換head
中CSS和JS外鏈位置
仍然發現不了什麼
把CSS放head
,JS放</body>
前
發現FP
居然在藍色和紅色虛線前面出現,經過這點能夠肯定,FP
還跟JS外鏈的位置有關,繼續:
JS外鏈放head
,CSS放</body>
前
發現又跟第一二種狀況同樣了,因此這種用法是不可取的。
CSS和JS都放</body>
前,且CSS緊貼在div
後面,JS在CSS後面:
能夠發現FP
竟然更快觸發,可是我鼠標hover到綠色虛線後,仍然是白屏,只有等到CSS加載完成執行Parse Stylesheet
以後才顯示出內容(說明這種用法也不可取),難道body中的CSS也會影響?
掉換一下上面CSS和JS的位置:
發現此次FP
觸發並且立馬有內容,而等到CSS加載完成以後還會再從新渲染一次,嗯,看來body中的第一個JS腳本有貓膩,接下來的狀況對他特殊照顧。
CSS放head
中,JS放在div
節點中間:
哈哈,竟然只渲染了12倆字,說明瀏覽器會渲染body中腳本以前的內容,那會是哪一個腳本以前的內容呢?
在div之間都插入腳本
看來瀏覽器會提早渲染body
中第一個腳本前的內容(咱們就把body中的第一個外鏈腳本叫作【第一腳本】吧
),而且第一腳本還會在FP以後才執行。因此結合以前得出的結論,在CSSOM準備就緒以後,瀏覽器會提早渲染第一腳本前的內容,咱們能夠用第九種狀況來驗證:
這種狀況和上種沒什麼區別,只是增長了一個CSS,這個CSS中還會發出一個請求去加載其餘CSS(經過@import url()
的方式),因此CSS的加載時間很長。
經過結果能夠看出,123在CSS下載完成以後才渲染,而不是單獨渲染一個1,因此FP
必須得等到CSSOM
準備就緒以後纔會觸發,不然即便有第一腳本在也沒用。
因此到這裏,咱們總算能夠下結論了:
FP發生在body中第一個script腳本以前的CSS解析和JS執行完成以後。換句話說就是第一腳本以前的
DOM
和CSSOM
準備就緒以後,便會着手渲染第一腳本前的內容。
可是...你覺得到這裏就結束了?其實沒有。
這種狀況中,head
中既有JS也有CSS,body
中也有第一腳本存在:
注意上圖中的vue.js
是在head
中的,然後面的JS文件都在body
中,並且,vue.js
加載完成以後,body
中的JS還沒下載完成,這個時候咱們調換一下vue.js
和angular2.js
的位置:
看,這個時候又沒有提早渲染了,123等到全部JS文件都執行完以後才渲染,這種狀況除了驗證了第九點的結論,還能補充咱們的結論:
若是第一腳本前的JS和CSS加載完了,
body
中的腳本還未下載完成,那麼瀏覽器就會利用構建好的局部CSSOM
和DOM
提早渲染第一腳本前的內容(觸發FP
);若是第一腳本前的JS和CSS都還沒下載完成,body
中的腳本就已經下載完了,那麼瀏覽器就會在全部JS腳本都執行完以後才觸發FP。
到這裏本次探究就結束了,其實還有不少種狀況,感興趣的能夠本身去試試。
</body>
前(若是在head必須放JS,也儘可能減小他的大小,把大JS文件放</body>
前)。gzip
瞭解一下?),Stalled
、TTFB
和Content Download
的時間。Chrome
會渲染局部CSSOM
和DOM
First Paint
和DOMContentLoaded
、load
事件的觸發沒有絕對的關係,FP
可能在他們以前,也可能在他們以後,這取決於影響他們觸發的因素的各自時間(FP
:第一腳本
前CSSOM
和DOM
的構建速度;DOMContentLoaded
:HTML
文檔自身以及HTML
文檔中全部JS
、CSS
的加載速度;load
:圖片、音頻、視頻、字體的加載速度)。DOMContentLoaded
和load
事件也沒有強制的前後順序,DOMContentLoaded
通常在load
事件以前觸發,但也可能在load
事件以後觸發。第一腳本
前的CSS若是還會去加載字體文件,那麼即便CSSOM
和DOM
構建完成觸發FP
,頁面內容也會是空白,只有等到字體文件下載完成纔會出現內容(這也是咱們在打開一個加載了谷歌字體的網站會白屏很長時間的緣由)。CSS
外鏈之間是誰先加載完成誰先解析,可是JS
外鏈之間即便先加載完成,也得按順序執行。link
外鏈後面緊跟script
外鏈,須先等link parse
完成以後,script
纔會執行,即便script
先下載完成。script
後面緊跟link
,也是同樣,會等script
執行完以後,link
纔會parse
。script
以後緊跟幾個link
且script
比這幾個link
的下載時間都長,那script
執行完成以後link
是按順序執行。RRDL
:
RRDL
的時候,在D(Receive data)
這個步驟可能執行屢次。TTFB
:Time To First Byte
,第一個字節返回的時間,這個是對應send Request
到receive Response
這段時間。Hightest/High/Meduim/Low/Lowest
),通常HTML
文檔自身、head
中的CSS都是Hightest
,head
中JS通常是High
,而圖片通常是Low
,而設置了async/defer
的腳本通常是Low
,gif
圖片通常是Lowest
。
參考連接:
個人博客即將搬運同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=3akuvl3965mok