1. 程式人生 > >當我們在瀏覽器中輸入一個URL後,發生了什麼?

當我們在瀏覽器中輸入一個URL後,發生了什麼?

寫在開頭:這篇文章被我歸入部落格效能優化類別,是因為我認為如果我們要優化網站效能、提升使用者體驗,首要目標就是要知道使用者在本地請求並載入你的網頁的過程中,到底發生了什麼,在此基礎上我們才能更好的優化網頁。

圖源:知乎-張秋怡

瀏覽器解析並查詢快取

DNS查詢

  • DNS查詢順序如下,若其中一步成功則直接跳到建立連結部分:

    • 瀏覽器自身DNS

    • 作業系統DNS

    • 本地hosts檔案

    • 向域名伺服器傳送請求

建立連結

  • TCP三次握手(three-way handshaking)

    • 傳送方:SYN(synchronize)

    • 接收方:SYN/ACK(acknowledgement),確認資訊傳達

    • 傳送方:ACK - 確認接收方線上可收訊息,握手結束

    • Accept

TCP三次握手的的好處在於:傳送方可以確認接收方仍然線上,不會因為白髮送而浪費資源。

傳送HTTP請求

  • 報文首部(GET /index.html HTTP/1.1)

    • 方法

    • URL

    • HTTP版本

  • 空行(CR+LF)

  • 報文主體

注意: 1.HTTP是無連線

無狀態的,即HTTP在傳輸完成後就會斷開(HTTP1.1以前),並且不會記錄訪問者的狀態。

從HTTP/1.1開始才支援持久連線,即通訊一次以後連線不中斷。

正常來說,HTTP請求、響應方式為每請求一次就響應一次:

請求1 -> 響應1 -> 請求2 -> 響應2 -> 請求3 -> 響應3

若採用持久連線請求管線化方式:

請求1 -> 請求2 -> 請求3 -> 響應1 -> 響應2 -> 響應3

使用管線化的條件:

  1. 服務端需要支援管線化

  2. 只有GET和HEAD可以進行管線化,POST請求有所限制

  3. 管線化不會影響響應到來的順序

2.關於CR(Carriage Return,回車)和LF(Line Feed,換行)

Dos和Windows採用CR/LF表示下一行 UNIX/Linux採用LF表示下一行 MAC OS系統則採用CR表示下一行

伺服器傳送響應

  • 報文首部(HTTP/1.1 200 OK)

    • HTTP版本

    • 響應狀態碼

    • 狀態碼資訊

  • 空行(CR+LF)

  • 報文主體

客戶端收到頁面,瀏覽器渲染頁面

執行以下過程:

解析HTML

  • 根據DOCTYPE來確定文件型別(最常見的就是HTML5,注意如果是HTML4的話有嚴格和寬鬆模式之分)

  • 構建DOM樹(根據HTML構建類似於二叉樹的結構樹)

  • 下載資源

    • CSS - 構建CSSOM樹

    • js - 一般下載後立即執行,會阻塞頁面渲染

瀏覽器渲染

在聊瀏覽器渲染之前,我們先明確一個概念: 事實上,我們看到的頁面並不是直觀所見的一層圖頁,而是由許多DOM元素渲染層(Layers)組成的,如下圖。

頁面的渲染過程

所以一個的頁面的渲染過程由如下幾步構成:

  • 構建渲染樹(Render Tree): 根據DOM和CSSOM樹渲染,不可見元素不被會渲染

  • 佈局(layout): CPU根據渲染樹佈局計算元素的具體位置和大小,轉換成絕對畫素,並且根據樣式,分割成多個獨立的渲染層(Layers),將每一層對應到點陣圖中

  • 繪製(Paint): GPU根據每個渲染層(Layers)的點陣圖繪製每個點,即畫素填充,並且將所以渲染層快取,如果下次頁面變動但是渲染層沒變就不會觸發重繪。

  • 層級合成(Compositing): 顧名思義,即處理多層渲染層之間的關係,將其合成為一個完整的頁面。

重繪和重排

重繪(repaint):

  • 元素視覺表現屬性被改變即觸發重繪,如改變visibility,color等,不會影響到dom結構

reflow(重排):

  • 與repaint區別就是:所有影響dom的元素佈局的事件都會觸發重排。同時也會觸發repaint。

  • 這種開銷是非常昂貴的,導致效能下降是必然的,頁面元素越多效果越明顯。

reflow常見情況:

  • 增刪改DOM節點

  • 移動DOM的位置或是動畫顯示(所以儘量用canvas來做動畫)

  • 修改width、display等CSS樣式

  • resize視窗或是滾動的時候

  • 修改網頁預設字型(不建議)

  • display:none會觸發reflow和repaint,而visibility:hidden只會產生repaint

顯而易見,要提高頁面效能,首要目標就是減少重繪重排,具體方法包括但不限於以下幾種:

  • 壓縮DOM深度,以免內層元素改變而導致多個外層都改變。

  • 對於沒用的元素,儘量設定為display:none,減輕繪製壓力。

  • 在對DOM進行大量元素操作時,我們可以使用利用DocumentFragment物件進行操作,最後再一次性裝載進DOM結構中。

  • 指定img的大小:由於img是內聯元素,所以在載入後會改變寬高,嚴重的會導致整個頁面重排,所以最好在渲染前就設定好其寬高,或者讓其脫離文件流。

DOM渲染層(Layers)與GPU硬體加速

知道了瀏覽器頁面的渲染合成過程後,我們不難得出一個結論:

如果我們把會發生大量重繪重排的元素提取出來,單獨觸發一個渲染層(Layer),就不會把其他元素一起帶著重繪,這會大大提高頁面效能。

那麼如何觸發渲染層,讓GPU來加速繪製呢?最簡單的方法有以下三種:

will-change: transform;will-change: opacity;transform: translateZ(0);

PS:使用Layers來觸發GPU加速(硬體加速)也會帶來負面影響,如電量損失過快、佔用記憶體和GPU等。所以在使用中要注意不能濫用,在常觸發重繪和重排的元素上使用即可。