序言
隨著用戶量越來越多,業務方關于用戶體驗的要求也在不斷提高,首屏渲染時間就成為了一個提高用戶體驗的指標,減少用戶等待的時間,在一定程度上就會提高用戶的留存,
頁面加載渲染是怎樣的一個程序
參考自Chrome的First Paint
- 瀏覽器輸入url,瀏覽器發送請求到服務器,服務器將請求的HTML回傳給瀏覽器,
- 瀏覽器下載完成HTML(Finish Loading HTML)之后,便開始從上到下決議,
- 決議的程序中碰到css和js外鏈(其實HTML的下載也是這個流程)都會執行以下程序:
- Send Request:表示給這個外鏈對應的服務器發送請求
- Receive Response: 表示接收回應,這里是表示告訴瀏覽器可以開始從網路接收資料了
- Receive Data:表示開始接收資料
- Finish Loading: 表示已經完成下載資料,
- Parse Stylesheet/Evaluate(默認情況下js下載完成之后執行Evaluate,css下載完成后會進行Parse Stylesheet)
- 所有的css下載完成后Parse Stylesheet然后開始構建CSSOM DOM(檔案物件模型)和 CSSOM(CSS物件模型)會合并生成一個渲染樹(Render Tree)
- 根據渲染樹的內容計算處各個節點在網頁中的大小和位置(Layout,可以理解為“刻章”)
- 根據Layout繪制內容在瀏覽器上(Paint,可以理解為“蓋章”),

通過這個程序我們可以看到,整個頁面加載到渲染其實可以分為兩個部分,第一部分是資源的的加載,簡稱為RRDL;第二部分是頁面的渲染,
探討首屏渲染,我們也將從這兩個方面入手來討論,
首屏渲染是什么時候開始呢?
參考自Chrome的First Paint
先看一個結論,觸發首屏渲染的時機,會有兩種情況:
- 如果第一腳本前的JS和CSS加載完了,body中的腳本還未下載完成,那么瀏覽器就會利用構建好的區域CSSOM和DOM提前渲染第一腳本前的內容(觸發FP);
- 如果第一腳本前的JS和CSS都還沒下載完成,body中的腳本就已經下載完了,那么瀏覽器就會在所有JS腳本都執行完之后才觸發FP,
第一腳本:body中的第一個外鏈腳本,
所以,我們需要按照第一條的方式來安排頁面的內容,爭取在第一腳本前JS和CSS要盡快加載完,然后盡快進入首屏渲染,
如何加快首屏渲染?
參考自Chrome的First Paint
- CSS放在head中,JS放在</body>前(如果在head必須放JS,也盡量減少他的大小,把大JS檔案放</body>前),
- 減小head中CSS和JS大小(gzip了解一下?)
- 優化head中的JS和CSS外鏈的網路情況,減少Stalled、TTFB和Content Download的時間,
- 在第一腳本前使用骨架圖,可以減少用戶的白屏感知時間(對于使用JS插入模板來渲染的框架,建議將骨架圖的路由生成邏輯單獨提出來)
整個頁面的生命周期是怎樣的呢?
參考自頁面生命周期:DOMContentLoaded, load, beforeunload, unload決議
HTML頁面的生命周期有以下以下幾個重要事件:
- DOMContentLoaded :瀏覽器已經完全加載了 HTML,DOM 樹已經構建完畢,但是像是<img>和樣式表等外部資源可能并沒有下載完畢,
- load:瀏覽器已經加載了所有的資源(影像,樣式表等),
- beforeunload/unload :當用戶離開頁面的時候觸發,
- readyState :描述document的loading狀態,其實是對頁面加載事件的細化,比如loading表示DOMContentLoaded還沒有觸發,我們在這個時候注冊DOMContentLoaded事件是有效的,interactive后會觸發DOMContentLoaded,comoplete之后會觸發load事件,readyState的改變會觸發readystatechange事件,
- loading 加載:document仍在加載,
- interactive 互動 : 檔案已經完成加載,檔案已被決議,但是諸如影像,樣式表和框架之類的子資源仍在加載,
- complete :檔案和所有子資源已完成加載,狀態表示 load 事件即將被觸發,
并行下載,串行執行
頁面依賴外部資源下載是并行的,但是DOM樹決議、js執行和首屏渲染卻是串行的,理解整個程序,就能明白為什么Head中不要放JS的原因,以及CSS為什么要盡量小,
頁面渲染的大致程序為,先下載決議HTML并建立DOM樹,再決議css繪制渲染樹,前者搭建頁面結構,后者增添頁面樣式,而在建立DOM樹的程序就會遇到諸如img、外聯css和script標簽,此時就要加載外部資源了,加載資源是由單獨的下載執行緒進行異步加載的,瀏覽器會并行加載,不過具體并行最大數量是有一定限制的,不同瀏覽器可能不一樣,
但是加載css和js資源比較特殊,它們的加載會影響頁面渲染,css加載不會阻塞DOM樹決議,但會阻塞渲染(這是由于渲染依賴于css,如果不等css加載完就渲染的話那么等css加載決議完又得重新渲染,可能又要重繪或者回流),對于js資源的加載,則會阻塞DOM樹的構建和渲染,除非設定了script標簽的異步屬性,
放在head中會在決議DOM樹和渲染頁面前就加載,并阻塞頁面,js正常情況下加載完就會立即執行,在js腳本中只能訪問當前<script>以上的DOM,腳本執行結束后再繼續決議DOM,js執行引擎和頁面渲染是由不同的執行緒來執行,但這兩者是互斥的,也就是說js執行程序是無法構建DOM和渲染頁面的,這是一種優化機制,由于js可能會對DOM及樣式進行修改,如果決議js程序中同時構建DOM,就可能造成前后內容不一致或者重復構建,所以應該把script放在body中,使頁面更快完成渲染,
一些常識
參考自Chrome的First Paint
- Chrome會渲染區域CSSOM和DOM,整個特別重要,理解這個概念,才能理解為什么不需要DOMContentLoaded,就可以進行首屏渲染了,不然總是會誤解,首屏渲染必須在DOMContentLoaded之后,
- 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:
- R:send Request,發送資源請求
- R:receive Response,接收到服務端回應
- D:receive Data,開始接受服務端資料(一個資源可能執行多次)
- L:finish Loading,完成資源下載
- 瀏覽器在RRDL的時候,在D(Receive data)這個步驟可能執行多次,
- TTFB:Time To First Byte,第一個位元組回傳的時間,這個是對應send Request到receive Response這段時間,
- 瀏覽器會給HTML中的資源檔案進行等級分類(Hightest/High/Meduim/Low/Lowest),一般HTML檔案自身、head中的CSS都是Hightest,head中JS一般是High,而圖片一般是Low,而設定了async/defer的腳本一般是Low,gif圖片一般是Lowest,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/113686.html
標籤:Html/Css
下一篇:CSS語法規范一
