《瀏覽器工作原理與實踐》<04>從輸入URL到頁面展示,這中間發生了什么?


在瀏覽器里,從輸入 URL 到頁面展示,這中間發生了什么? ”這是一道經典的面試題,能比較全面地考察應聘者知識的掌握程度,其中涉及到了網絡、操作系統、Web 等一系列的知識。

在面試應聘者時也必問這道題,但遺憾的是大多數人只能回答其中部分零散的知識點,並不能將這些知識點串聯成線,無法系統而又全面地回答這個問題。

那么今天我們就一起來探索下這個流程,下圖是梳理出的“從輸入 URL 到頁面展示完整流程示意圖”:

從圖中可以看出,整個過程需要各個進程之間的配合,所以在開始正式流程之前,我們還是先來快速回顧下瀏覽器進程、渲染進程和網絡進程的主要職責。

1.瀏覽器進程主要負責用戶交互、子進程管理和文件儲存等功能。

2.網絡進程是面向渲染進程和瀏覽器進程等提供網絡下載功能。

3.渲染進程的主要職責是把從網絡下載的 HTML、JavaScript、CSS、圖片等資源解析為可以顯示和交互的頁面。因為渲染進程所有的內容都是通過網絡獲取的,會存在一些惡意代碼利用瀏覽器漏洞對系統進行攻擊,所以運行在渲染進程里面的代碼是不被信任的。這也是為什么 Chrome 會讓渲染進程運行在安全沙箱里,就是為了保證系統的安全。

通過分析,這個過程可以大致描述為如下:

首先,用戶從瀏覽器進程里輸入請求信息。

然后,網絡進程發起 URL 請求

服務器響應 URL 請求之后,瀏覽器進程就又要開始准備渲染進程了。

渲染進程准備好之后,就需要通知瀏覽器進程:“我已經准備好了,可以向用戶展示頁面狀態了”,我們把這個渲染進程通知瀏覽器進程的階段,稱為“提交文檔”階段

瀏覽器進程接收到渲染進程“提交文檔”的消息之后,便開始移除之前舊的文檔,然后通知渲染進程“文檔已提交”,此時渲染進程便進入了“解析頁面”階段

這其中,用戶發出 URL 請求到頁面開始解析的這個過程,就叫做導航

 

從輸入 URL 到頁面展示

現在我們知道了瀏覽器幾個主要進程的職責,還有在導航過程中需要經歷的幾個主要的階段,下面我們就來詳細分析下這些階段,同時也就解答了開頭所說的那道經典的面試題。

 

1. 用戶輸入

當用戶在地址欄中輸入一個查詢關鍵字時,地址欄會判斷輸入的關鍵字是搜索內容還是請求的 URL

如果是搜索內容,地址欄會使用瀏覽器默認的搜索引擎,來合成新的帶搜索關鍵字的 URL

如果判斷輸入內容符合 URL 規則,比如輸入的是 time.geekbang.org,那么地址欄會根據規則,把這段內容加上協議,合成為完整的 URL,如 https://time.geekbang.org。

當用戶輸入關鍵字並鍵入回車之后,這意味着當前頁面即將要被替換成新的頁面,不過在這個流程繼續之前,瀏覽器還給了當前頁面一次執行 beforeunload 事件的機會,beforeunload 事件允許頁面在退出之前執行一些數據清理操作,還可以詢問用戶是否要離開當前頁面,比如當前頁面可能有未提交完成的表單等情況,因此用戶可以通過 beforeunload 事件來取消導航,讓瀏覽器不再執行任何后續工作。當前頁面沒有監聽 beforeunload 事件或者同意了繼續后續流程,那么瀏覽器便進入下圖的狀態:

 

 

從圖中可以看出,當瀏覽器剛開始加載一個地址之后,標簽頁上的圖標便進入了加載狀態。但此時圖中頁面顯示的依然是之前打開的頁面內容,並沒立即替換為極客時間的頁面。因為需要等待提交文檔階段,頁面內容才會被替換

 

2. URL 請求過程

接下來,便進入了頁面資源請求過程。這時,瀏覽器進程會通過進程間通信(IPC)URL 請求發送至網絡進程網絡進程接收到 URL 請求后,會在這里發起真正的 URL 請求流程。那具體流程是怎樣的呢?

首先,網絡進程會查找本地緩存是否緩存了該資源。如果有緩存資源,那么直接返回資源給瀏覽器進程;如果在緩存中沒有查找到資源,那么直接進入網絡請求流程。這請求前的第一步是要進行 DNS 解析,以獲取請求域名的服務器 IP 地址如果請求協議是 HTTPS,那么還需要建立 TLS 連接。

接下來就是利用 IP 地址和服務器建立 TCP 連接。連接建立之后,瀏覽器端會構建請求行、請求頭等信息,並把和該域名相關的 Cookie 等數據附加到請求頭中,然后向服務器發送構建的請求信息

服務器接收到請求信息后,會根據請求信息生成響應數據(包括響應行、響應頭和響應體等信息),並發給網絡進程。等網絡進程接收了響應行和響應頭之后,就開始解析響應頭的內容了。(為了方便講述,下面我將服務器返回的響應頭和響應行統稱為響應頭。)

 

(1)重定向

在接收到服務器返回的響應頭后,網絡進程開始解析響應頭,如果發現返回的狀態碼是 301 或者 302,那么說明服務器需要瀏覽器重定向到其他 URL。這時網絡進程會從響應頭的 Location 字段里面讀取重定向的地址,然后再發起新的 HTTP 或者 HTTPS 請求,一切又重頭開始了。

curl -I http://time.geekbang.org/

curl -I + URL的命令是接收服務器返回的響應頭的信息。執行命令后,我們看到服務器返回的響應頭信息如下:

 

從圖中可以看出,極客時間服務器會通過重定向的方式把所有 HTTP 請求轉換為 HTTPS 請求。也就是說你使用 HTTP 向極客時間服務器請求時,服務器會返回一個包含有 301 或者 302 狀態碼響應頭,並把響應頭的 Location 字段中填上 HTTPS 的地址,這就是告訴了瀏覽器要重新導航到新的地址上。

 

下面我們再使用 HTTPS 協議對極客時間發起請求,看看服務器的響應頭信息是什么樣子的。

curl -I https://time.geekbang.org/

 

從圖中可以看出,服務器返回的響應頭的狀態碼是 200,這是告訴瀏覽器一切正常,可以繼續往下處理該請求了。

 

(2)響應數據類型處理

在處理了跳轉信息之后,我們繼續導航流程的分析。URL 請求的數據類型,有時候是一個下載類型,有時候是正常的 HTML 頁面,那么瀏覽器是如何區分它們呢

答案是 Content-Type。Content-Type 是 HTTP 頭中一個非常重要的字段, 它告訴瀏覽器服務器返回的響應體數據是什么類型,然后瀏覽器會根據 Content-Type 的值來決定如何顯示響應體的內容。這里我們還是以極客時間為例,看看極客時間官網返回的 Content-Type 值是什么。在終端輸入以下命令:

這里我們還是以極客時間為例,看看極客時間官網返回的 Content-Type 值是什么。在終端輸入以下命令:

curl -I https://time.geekbang.org/

從圖中可以看到,響應頭中的 Content-type 字段的值是 text/html,這就是告訴瀏覽器,服務器返回的數據是 HTML 格式。

如果服務器配置 Content-Type 不正確,比如將 text/html 類型配置成 application/octet-stream 類型,那么瀏覽器可能會曲解文件內容,比如會將一個本來是用來展示的頁面,變成了一個下載文件。

所以,不同 Content-Type 的后續處理流程也截然不同。如果 Content-Type 字段的值被瀏覽器判斷為下載類型那么該請求會被提交給瀏覽器的下載管理器,同時該 URL 請求的導航流程就此結束。但如果是 HTML,那么瀏覽器則會繼續進行導航流程由於 Chrome 的頁面渲染是運行在渲染進程中的,所以接下來就需要准備渲染進程了

 

3. 准備渲染進程

默認情況下,Chrome 會為每個頁面分配一個渲染進程,也就是說,每打開一個新頁面就會配套創建一個新的渲染進程。但是,也有一些例外,在某些情況下,瀏覽器會讓多個頁面直接運行在同一個渲染進程中。比如我從極客時間的首頁里面打開了另外一個頁面——算法訓練營,我們看下圖的 Chrome 的任務管理器截圖:

 

從圖中可以看出,打開的這三個頁面都是運行在同一個渲染進程中,進程 ID 是 23601

那什么情況下多個頁面會同時運行在一個渲染進程中呢?

要解決這個問題,我們就需要先了解下什么是同一站點(same-site)。具體地講,我們將“同一站點”定義為根域名(例如,geekbang.org)加上協議(例如,https:// 或者 http://),還包含了該根域名下的所有子域名和不同的端口,比如下面這三個:

https://time.geekbang.org
https://www.geekbang.org
https://www.geekbang.org:8080

它們都是屬於同一站點,因為它們的協議都是 HTTPS,而且根域名也都是 geekbang.org。

 

Chrome 的默認策略是,每個標簽對應一個渲染進程。但如果從一個頁面打開了另一個新頁面,而新頁面和當前頁面屬於同一站點的話,那么新頁面會復用父頁面的渲染進程。官方把這個默認策略叫 process-per-site-instance。

 

總結來說,打開一個新頁面采用的渲染進程策略就是:

通常情況下,打開新的頁面都會使用單獨的渲染進程;如果從 A 頁面打開 B 頁面,且 A 和 B 都屬於同一站點的話,那么 B 頁面復用 A 頁面的渲染進程

如果是其他情況,瀏覽器進程則會為 B 創建一個新的渲染進程

渲染進程准備好之后,還不能立即進入文檔解析狀態,因為此時的文檔數據還在網絡進程中,並沒有提交給渲染進程,所以下一步就進入了提交文檔階段

 

4. 提交文檔

在上面我們分析過了,渲染進程准備好之后,它就會通知瀏覽器進程,可以替換當前舊的文檔了,具體地講,需要經過下列幾個步驟:

1.首先“提交文檔”的消息是由渲染進程發出給瀏覽器進程的,這是告訴瀏覽器進程,它已經准備好了,可以執行解析渲染等后續操作了。

2.瀏覽器進程接收到當前渲染進程的“提交文檔”消息后,便開始清理當前的舊文檔,然后會發出“確認提交”的消息給渲染進程。同時,瀏覽器進程會更新瀏覽器界面狀態,包括了安全狀態、地址欄的 URL、前進后退的歷史狀態,並更新 Web 頁面。

3.當渲染進程接收到“確認提交”的消息后,便開始執行解析數據、下載子資源等后續流程,並實時向瀏覽器進程更新最新的渲染狀態。

其中,當瀏覽器進程確認提交之后,更新內容如下圖所示:

這也就解釋了為什么在瀏覽器的地址欄里面輸入了一個地址后,之前的頁面沒有立馬消失,而是要加載一會兒才會更新頁面。

到這里,一個完整的導航流程就“走”完了,這之后就要進入渲染階段了

 

5. 渲染階段

一旦文檔被提交,渲染進程便開始頁面解析和子資源加載了,關於這個階段的完整過程,我會在下一篇文章中來專門介紹。這里你只需要先了解一旦頁面生成完成,渲染進程會發送一個消息給瀏覽器進程,瀏覽器接收到消息后,會停止標簽圖標上的加載動畫。如下所示:

至此,一個完整的頁面就生成了。那文章開頭的“從輸入 URL 到頁面展示,這中間發生了什么?”這個過程極其“串聯”的問題也就解決了。

 

后續會講講頁面的渲染過程

 

 

 

 

 

注: 本文出自極客時間(瀏覽器工作原理與實踐),請大家多多支持李兵老師。如有侵權,請及時告知。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM