經典面試題:從輸入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 Tree
與CSSOM 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
覺得文章不錯,可以點個在看呀_另外歡迎留言交流~