超詳細講解頁面載入過程

前端南玖發表於2021-11-09

經典面試題:從輸入URL到頁面載入完成之間的過程。你會發現,這題不論大廠小廠,都會問,為什麼?

因為它不僅可以考察面試者的知識廣度還能考察面試者的知識深度。

前言

如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖 第一時間獲取最新的文章~

在上一篇文章這些瀏覽器面試題,看看你能回答幾個?,我也提到過這個經典面試題。下面我們一起來看看吧~

說一說從輸入URL到頁面呈現發生了什麼?(知識點)

這個題可以說是面試最常見也是一道可以無限難的題了,一般面試官出這道題就是為了考察你的前端知識的深度與廣度。

1.瀏覽器接受URL開啟網路請求執行緒(涉及到:瀏覽器機制,執行緒與程式等)

2.開啟網路執行緒到發出一個完整的http請求(涉及到:DNS解析,TCP/IP請求,5層網路協議等)

3.從伺服器接收到請求到對應後臺接受到請求(涉及到:負載均衡,安全攔截,後臺內部處理等)

4.後臺與前臺的http互動(涉及到:http頭,響應碼,報文結構,cookie等)

5.快取問題(涉及到:http強快取與協商快取等)(請看上一篇文章這些瀏覽器面試題,看看你能回答幾個?

6.瀏覽器接受到http資料包後的解析流程(涉及到html詞法分析,解析成DOM樹,解析CSS生成CSSOM樹,合併生成render渲染樹。然後layout佈局,painting渲染,複合圖層合成,GPU繪製,等)

在瀏覽器位址列輸入URL

當我們在瀏覽器位址列輸入URL地址後,瀏覽器會開一個執行緒來對我們輸入的URL進行解析處理。

瀏覽器中的各個程式及作用:(多程式)

  • 瀏覽器程式:負責管理標籤頁的建立銷燬以及頁面的顯示,資源下載等。
  • 第三方外掛程式:負責管理第三方外掛。
  • GPU程式:負責3D繪製與硬體加速(最多一個)。
  • 渲染程式:負責頁面文件解析(HTML,CSS,JS),執行與渲染。(可以有多個)

DNS域名解析

為什麼需要DNS域名解析?

因為我們在瀏覽器中輸入的URL通常是一個域名,並不會直接去輸入IP地址(純粹因為域名比IP好記),但我們的計算機並不認識域名,它只知道IP,所以就需要這一步操作將域名解析成IP。

URL組成部分

  • protocol:協議頭,比如http,https,ftp等;
  • host:主機域名或者IP地址;
  • port:埠號;
  • path:目錄路徑;
  • query:查詢的引數;
  • hash:#後邊的hash值,用來定位某一個位置。

解析過程

  • 首先會檢視瀏覽器DNS快取,有的話直接使用瀏覽器快取

  • 沒有的話就查詢計算機本地DNS快取(localhost)

  • 還沒有就詢問遞迴式DNS伺服器(就是網路提供商,一般這個伺服器都會有自己的快取)

  • 如果依然沒有快取,那就需要通過 根域名伺服器 和 TLD域名伺服器 再到對應的 權威DNS伺服器 找記錄,並快取到 遞迴式伺服器,然後 遞迴伺服器 再將記錄返回給本地

⚠️注意:

DNS解析是非常耗時的,如果頁面中需要解析的域名過多,是非常影響頁面效能的。考慮使用dns與載入或減少DNS解析進行優化。

傳送HTTP請求

拿到了IP地址後,就可以發起HTTP請求了。HTTP請求的本質就是TCP/IP的請求構建。建立連線時需要3次握手進行驗證,斷開連結也同樣需要4次揮手進行驗證,保證傳輸的可靠性

3次握手

  • 第一次握手:客戶端傳送位碼為 SYN = 1(SYN 標誌位置位),隨機產生初始序列號 Seq = J 的資料包到伺服器。伺服器由 SYN = 1(置位)知道,客戶端要求建立聯機。
  • 第二次握手:伺服器收到請求後要確認聯機資訊,向客戶端傳送確認號Ack = (客戶端的Seq +1,J+1),SYN = 1,ACK = 1(SYN,ACK 標誌位置位),隨機產生的序列號 Seq = K 的資料包。
  • 第三次握手:客戶端收到後檢查 Ack 是否正確,即第一次傳送的 Seq +1(J+1),以及位碼ACK是否為1。若正確,客戶端會再傳送 Ack = (伺服器端的Seq+1,K+1),ACK = 1,以及序號Seq為伺服器確認號J 的確認包。伺服器收到後確認之前傳送的 Seq(K+1) 值與 ACK= 1 (ACK置位)則連線建立成功。

直白理解:

(客戶端:hello,你是server麼?服務端:hello,我是server,你是client麼 客戶端:yes,我是client 建立成功之後,接下來就是正式傳輸資料。)

4次揮手

  • 客戶端傳送一個FIN Seq = M(FIN置位,序號為M)包,用來關閉客戶端到伺服器端的資料傳送。
  • 伺服器端收到這個FIN,它發回一個ACK,確認序號Ack 為收到的序號M+1。
  • 伺服器端關閉與客戶端的連線,傳送一個FIN Seq = N 給客戶端。
  • 客戶端發回ACK 報文確認,確認序號Ack 為收到的序號N+1。

直白理解:

(主動方:我已經關閉了向你那邊的主動通道了,只能被動接收了 被動方:收到通道關閉的資訊 被動方:那我也告訴你,我這邊向你的主動通道也關閉了 主動方:最後收到資料,之後雙方無法通訊)

五層網路協議

1、應用層(DNS,HTTP):DNS解析成IP併傳送http請求;

2、傳輸層(TCP,UDP):建立TCP連線(3次握手);

3、網路層(IP,ARP):IP定址;

4、資料鏈路層(PPP):封裝成幀;

5、物理層(利用物理介質傳輸位元流):物理傳輸(通過雙絞線,電磁波等各種介質)。

OSI七層框架:物理層、資料鏈路層、網路層、傳輸層、會話層、表示層、應用層

伺服器接收請求做出響應

HTTP 請求到達伺服器,伺服器進行對應的處理。 最後要把資料傳給瀏覽器,也就是返回網路響應。

跟請求部分類似,網路響應具有三個部分:響應行、響應頭和響應體。

響應完成之後怎麼辦?TCP 連線就斷開了嗎?

不一定。這時候要判斷Connection欄位, 如果請求頭或響應頭中包含Connection: Keep-Alive
表示建立了持久連線,這樣TCP連線會一直保持,之後請求統一站點的資源會複用這個連線。否則斷開TCP連線, 請求-響應流程結束。

狀態碼

狀態碼是由3位陣列成,第一個數字定義了響應的類別,且有五種可能取值:

  • 1xx:指示資訊–表示請求已接收,繼續處理。
  • 2xx:成功–表示請求已被成功接收、理解、接受。
  • 3xx:重定向–要完成請求必須進行更進一步的操作。
  • 4xx:客戶端錯誤–請求有語法錯誤或請求無法實現。
  • 5xx:伺服器端錯誤–伺服器未能實現合法的請求。
    平時遇到比較常見的狀態碼有:200, 204, 301, 302, 304, 400, 401, 403, 404, 422, 500(分別表示什麼請自行查詢)。

伺服器返回相應檔案

請求成功後,伺服器會返回相應的網頁,瀏覽器接收到響應成功的報文後便開始下載網頁,至此,網路通訊結束。

瀏覽器解析渲染頁面

瀏覽器在接收到HTML,CSS,JS檔案之後,它是如何將頁面渲染在螢幕上的?

解析HTML構建DOM Tree

瀏覽器在拿到伺服器返回的網頁之後,首先會根據頂部定義的DTD型別進行對應的解析,解析過程將被交給內部的GUI渲染執行緒來處理。

DTD(Document Type Definition)文件型別定義

常見的文件型別定義

//HTML5文件定義
<!DOCTYPE html>
//用於XHTML 4.0 的嚴格型 
<!DOCTYPE HTMLPUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
//用於XHTML 4.0 的過渡型 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
//用於XHTML 1.0 的嚴格型 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
//用於XHTML 1.0 的過渡型
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

HTML直譯器的工作就是將網路或者本地磁碟獲取的HTML網頁或資源從位元組流解釋成DOM樹?結構

通過上圖可以清楚的瞭解這一過程:首先是位元組流,經過解碼之後是字元流,然後通過詞法分析器會被解釋成詞語(Tokens),之後經過語法分析器構建成節點,最後這些節點被組建成一顆 DOM 樹。

對於執行緒化的直譯器,字元流後的整個解釋、佈局和渲染過程基本會交給一個單獨的渲染執行緒來管理(不是絕對的)。由於 DOM 樹只能在渲染執行緒上建立和訪問,所以構建 DOM 樹的過程只能在渲染執行緒中進行。但是,從字串到詞語這個階段可以交給單獨的執行緒來做,Chrome 瀏覽器使用的就是這個思想。在解釋成詞語之後,Webkit 會分批次將結果詞語傳遞迴渲染執行緒。

這個過程中,如果遇到的節點是 JS 程式碼,就會呼叫 JS引擎 對 JS程式碼進行解釋執行,此時由於 JS引擎GUI渲染執行緒 的互斥,GUI渲染執行緒 就會被掛起,渲染過程停止,如果 JS 程式碼的執行中對DOM樹進行了修改,那麼DOM的構建需要從新開始

如果節點需要依賴其他資源,圖片/CSS等等,就會呼叫網路模組的資源載入器來載入它們,它們是非同步的,不會阻塞當前DOM樹的構建

如果遇到的是 JS 資源URL(沒有標記非同步),則需要停止當前DOM的構建,直到 JS 的資源載入並被 JS引擎 執行後才繼續構建DOM

解析CSS構建CSSOM Tree

CSS直譯器會將CSS檔案解釋成內部表示結構,生成CSS規則樹,這個過程也是和DOM解析類似的,CSS 位元組轉換成字元,接著詞法解析與法解析,最後構成 CSS物件模型(CSSOM) 的樹結構

構建渲染樹(Render Tree)

DOM TreeCSSOM Tree都構建完畢後,接著將它們合併成渲染樹(Render Tree)渲染樹 只包含渲染網頁所需的節點,然後用於計算每個可見元素的佈局,並輸出給繪製流程,將畫素渲染到螢幕上。

渲染(佈局,繪製,合成)

  • 計算CSS樣式 ;
  • 構建渲染樹 ;
  • 佈局,主要定位座標和大小,是否換行,各種position overflow z-index屬性 ;
  • 繪製,將影像繪製出來。

這個過程比較複雜,涉及到兩個概念: reflow(迴流)和repain(重繪)。DOM節點中的各個元素都是以盒模型的形式存在,這些都需要瀏覽器去計算其位置和大小等,這個過程稱為relow;當盒模型的位置,大小以及其他屬性,如顏色,字型,等確定下來之後,瀏覽器便開始繪製內容,這個過程稱為repain。頁面在首次載入時必然會經歷reflow和repain。reflow和repain過程是非常消耗效能的,尤其是在移動裝置上,它會破壞使用者體驗,有時會造成頁面卡頓。所以我們應該儘可能少的減少reflow和repain。

這裡Reflow和Repaint的概念是有區別的:

(1)Reflow:即迴流。一般意味著元素的內容、結構、位置或尺寸發生了變化,需要重新計算樣式和渲染樹。

(2)Repaint:即重繪。意味著元素髮生的改變只是影響了元素的一些外觀之類的時候(例如,背景色,邊框顏色,文字顏色等),此時只需要應用新樣式繪製這個元素就可以了。

迴流的成本開銷要高於重繪,而且一個節點的迴流往往回導致子節點以及同級節點的迴流, 所以優化方案中一般都包括,儘量避免迴流。

迴流一定導致重繪,但重繪不一定會導致迴流

合成(composite)

最後一步合成( composite ),這一步驟瀏覽器會將各層資訊傳送給GPU,GPU將各層合成,顯示在螢幕上

普通圖層和複合圖層

可以簡單的這樣理解,瀏覽器渲染的圖層一般包含兩大類:普通圖層以及複合圖層

首先,普通文件流內可以理解為一個複合圖層(這裡稱為預設複合層,裡面不管新增多少元素,其實都是在同一個複合圖層中)

其次,absolute佈局(fixed也一樣),雖然可以脫離普通文件流,但它仍然屬於預設複合層

然後,可以通過硬體加速的方式,宣告一個新的複合圖層,它會單獨分配資源
(當然也會脫離普通文件流,這樣一來,不管這個複合圖層中怎麼變化,也不會影響預設複合層裡的迴流重繪)

可以簡單理解下:GPU中,各個複合圖層是單獨繪製的,所以互不影響,這也是為什麼某些場景硬體加速效果一級棒

可以Chrome原始碼除錯 -> More Tools -> Rendering -> Layer borders中看到,黃色的就是複合圖層資訊。

推薦閱讀

這些瀏覽器面試題,看看你能回答幾個?
這一次帶你徹底瞭解前端本地儲存
面試官:說一說前端路由與後端路由的區別
JavaScript之原型與原型鏈
Javascript深入之作用域與閉包
this指向與call,apply,bind

覺得文章不錯,可以點個在看呀_另外歡迎留言交流~

相關文章