瀏覽器渲染邏輯和重繪、迴流解析
瀏覽器從輸入url 到 渲染出界面 中間經歷的過程
1 DNS 查詢
2 TCP 連線
3 HTTP 請求即響應
4 伺服器響應
5 客戶端渲染
瀏覽器是如何渲染UI的?(客戶端的渲染)
1 瀏覽器接收到 html 和 css 檔案 ,並對html進行解析,生成 DOM 樹 (遇到link,script 會造成阻塞)
2 同時對css進行解析,生成 CSSOM 樹
3 將DOM樹 和 CSSOM樹 合成 Render 樹(渲染樹)
4 根據渲染樹來佈局,以計算每個節點的幾何資訊。(定位座標和大小,是否換行,各種position, overflow, z-index屬性)
5 呼叫GPU進行繪圖,遍歷render tree的每個節點,將元素顯示到螢幕上
css的載入和解析不會阻塞html文件的解析
css的解析會阻塞js的執行,必須等到CSSOM生成後才能執行js
js的執行會阻塞html文件的解析
html一邊解析一邊顯示
css必須完全解析完畢才能進入生成渲染樹環節
重繪
Repaint——螢幕的一部分要重畫,比如某個CSS的背景色變了。但是元素的幾何尺寸沒有變。
迴流、重排
Reflow——意味著元件的幾何尺寸變了,我們需要重新驗證並計算Render Tree。是Render Tree的一部分或全部發生了變化。這就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式佈局,所以,如果某元件的幾何尺寸發生了變化,需要重新佈局,也就叫reflow)reflow 會從這個root frame開始遞迴往下,依次計算所有的結點幾何尺寸和位置,在reflow過程中,可能會增加一些frame,比如一個文字字串必需被包裝起來。
迴流的原因:
1 Initial。網頁初始化的時候。
2 Incremental。一些Javascript在操作DOM Tree時。
3 Resize。其些元件的尺寸變了。
4 StyleChange。如果CSS的屬性發生變化了。
5 Dirty。幾個Incremental的reflow發生在同一個frame的子樹上。
注: 一般來說,瀏覽器會把這樣的操作積攢一批,然後做一次reflow,這又叫非同步reflow或增量非同步reflow。但是有些情況瀏覽器是不會這麼做的,比如:resize視窗,改變了頁面預設的字型,等。對於這些操作,瀏覽器會馬上進行reflow。
link 標籤會阻塞html的解析
script 標籤會阻塞html的解析
由於 DOM樹和CSSOM才能生出渲染樹,所以 css越早提供越好,一般推薦link標籤寫到head中,script 標籤寫到文件底部
script標籤非同步載入 defer 、 async屬性,不會影響DOM樹的構建
defer
<script src="app1.js" defer></script>
<script src="app2.js" defer></script>
<script src="app3.js" defer></script>
defer 屬性表示延遲執行引入的 JavaScript,即這段 JavaScript 載入時 HTML 並未停止解析,這兩個過程是並行的。整個 document 解析完畢且 defer-script 也載入完成之後(這兩件事情的順序無關),會執行所有由 defer-script 載入的 JavaScript 程式碼,然後觸發 DOMContentLoaded 事件。
defer 不會改變 script 中程式碼的執行順序,示例程式碼會按照 1、2、3 的順序執行。所以,defer 與相比普通 script,有兩點區別:載入 JavaScript 檔案時不阻塞 HTML 的解析,執行階段被放到 HTML 標籤解析完成之後。
async
<script src="app.js" async></script>
<script src="ad.js" async></script>
<script src="statistics.js" async></script>
async 屬性表示非同步執行引入的 JavaScript,與 defer 的區別在於,如果已經載入好,就會開始執行——無論此刻是 HTML 解析階段還是 DOMContentLoaded 觸發之後。需要注意的是,這種方式載入的 JavaScript 依然會阻塞 load 事件。換句話說,async-script 可能在 DOMContentLoaded 觸發之前或之後執行,但一定在 load 觸發之前執行。
從上一段也能推出,多個 async-script 的執行順序是不確定的。值得注意的是,向 document 動態新增 script 標籤時,async 屬性預設是 true