流程圖
這題扎眼看上去沒問題,無非是 HTTP 請求到瀏覽器渲染,但可以聊的東西很多。我想它的執行順序是,使用者輸入——開始導航——HTTP請求——瀏覽器渲染。其中使用者輸入、開始導航、瀏覽器渲染是瀏覽器方面的知識點,HTTP請求是 HTTP 方面的知識點
以下就是從輸入 url 到看到頁面的整個流程圖
原圖地址:https://s2.loli.net/2022/04/2...
前言
瞭解"開始導航"之前,需要先知道瀏覽器架構,簡單來說,現代瀏覽器由1個瀏覽器主程式、1個GPU程式、多個渲染程式、多個外掛程式、網路程式、音訊程式、儲存程式組成
下圖是李兵在《瀏覽器工作原理與實踐》中所示,展示 Chrome 瀏覽器的架構
以及未來現代瀏覽器架構示意圖:
文章現代瀏覽器內部揭祕中有一張圖,是這樣描述的
圖中表明瀏覽器主程式包含了 UI 執行緒、網路執行緒、儲存執行緒,與李兵的觀點有所不同。那以誰為準呢?以時間為準,李兵的專欄是19年所寫,而《現代瀏覽器內部解密》是 18 年的文章,站在 2022 年的背景,現代瀏覽器,UI、網路、儲存等都已升級為程式,而非是瀏覽器主程式中的執行緒
使用者輸入
當使用者在位址列中輸入一個字串時,位址列會判斷輸入的關鍵字是搜尋內容,還是請求的URL
如果是搜尋內容,位址列會使用瀏覽器預設的搜尋引擎,合成新的帶搜尋關鍵字的URL
- 例如在chrome中搜長澤雅美
- 如果輸入內容符合 URL 規則,例如輸入
azhubaby.com
,那麼位址列會根據規則,把這段內容加上協議合成完成的 URL,如https://azhubaby.com
當使用者輸入關鍵字並鍵入回車之後,意味著當前頁面將替換為新的頁面,此時瀏覽器中有個 API——beforeunload,它允許頁面在離開之前觸發是否一個確認對話方塊。這裡使用此API,可讓瀏覽器不再導航
// 監聽離開頁面前的事件
window.addEventListener('beforeunload', (event) => {
event.preventDefault();
event.returnValue = '';
})
可在這裡看看 beforeunload 的demo
從瀏覽器架構分工上講,當使用者輸入字串時是 UI 程式(老一點的瀏覽器是瀏覽器主程式)在運作
開始導航
當敲下 Enter 鍵時,UI 程式將指揮權交接給了網路程式。網路程式接受請求指令前,會先查詢本地快取是否有快取。如果有快取該資源,那麼直接返回資源給瀏覽器程式;如果在快取中沒找到該資源,那麼則正式進入HTTP請求階段
關於HTTP快取方面的知識可以看看這篇——面試常客:HTTP 快取
HTTP請求
之前寫過一篇TCP/IP 協議及網路分層模型,講述了 TCP/IP 網路分層協議,它就像搭積木一樣,每一層需要下一層的支撐,我們的 HTTP 請求是其 HTTP 協議的應用,需要先連線傳輸層(TCP)以及更底層網路互連層(IP)
而IP從哪裡來,通過 DNS, 使其域名 和 IP 做對映
我們使用倒推法可以理清“路線”:
HTTP 請求 —— HTTP 協議連線 —— TCP 協議連線 —— IP 協議連線 —— 需要知道 IP——DNS 做域名/IP對映
所以進入 HTTP 請求的第一步是 DNS 解析
DNS 解析
這裡對 DNS 不做過多概述,簡單來說,它的作用是用域名代替 IP 地址,符合人的記憶。輸入du.azhubaby.com
,表示 IP 地址 47.102.152.19
,你可以在命令列中 ping 一個域名,來求證一下結果
HTTP 請求之前的第一步是判斷 DNS 中是否有快取,如果有,直接返回 IP 地址;如果沒有,則進行 DNS 解析,並把結果 IP 快取到 DNS
有了 IP 地址後,IP 層連線成功,接下來就是 TCP 傳輸層
TCP 連線
這裡要看HTTP協議的版本,如果是 HTTP/1.1 的話,就要考慮TCP佇列否飽滿,因為 HTTP/1.1 最多允許一個域名連線 6 條TCP,太多了就要在等待TCP佇列中排隊;如果是 HTTP/2 的話,那就沒事,它允許TCP併發
這裡還要考慮到如果協議是 HTTPS 協議的話,還需要建立一條 TLS 連線
等真正 TCP 連線時,就聯想到網紅面試題:三次握手、四次揮手
三次握手、四次揮手
為什麼是三次握手和四次揮手,因為只有這樣才能讓雙方(客戶端和服務端)知道彼此的接收能力和傳送能力是沒問題的
步驟為:
- 客戶端提出建立連線,發出客戶端seq:
seq=client_isn
- 服務端收到訊息後返回
ack=client_isn+1
和服務端seq:seq=server_isn
- 客戶端收到後返回
ack=server_isn+1
表示收到了
可以理解為男女雙方確認關係,男女雙方要結婚,怎麼辦?先見父母得到父母認同,之前聽過這樣一句話:得不到父母祝福的婚姻是不幸福的(當然,不見父母直接結婚的也有,但不主流)
- 男方提出去女方家,帶上見面禮seq:
seq=男方的誠意
- 女方家收到見面禮後返回(給男方)紅包
ack=我們認可你啦
以及女方去男方家也帶上見面禮seq:seq=女方的誠意
- 男方家收到見面禮後返回(給女方的)紅包
ack=server_isn+1
這個叫確定關係。所以要又來又回三次,雙方都確保知道對方的誠意和自己的誠意
那什麼是四次揮手呢?
在斷開之前,需要進行四次揮手
為什麼要有四次揮手?
主要是為了確保雙方都知道對方斷開連線
具體步驟為:
- 客戶端第一次傳送訊息給服務端告訴它需要斷開連線
- 服務端收到訊息後返回訊息告訴客戶端:知道了,為了確保服務端收到了之前所有的 HTTP 請求,服務端需要等一等再斷開連線
- 服務端確認所有的HTTP請求都收到了,主動發訊息給客戶端:我這邊所有的請求都處理完了,我也可以斷開連線了
- 客戶端收到這個請求後,返回訊息告訴服務端:我知道,斷開連線吧
主要是為了確認雙方的接收能力和傳送能力是否正常、制定自己的初始化序列號為後面的可靠性傳送做準備
可以理解為一對男女要分手
- 女方提出分手,說你對我不好,我要分手
- 男方覺得需求合理,同意分手,但分手之前要把聯絡方式、合照、各種亂七八糟的的事情算清楚再分手
- 男方理清楚後,主動發訊息給女方,說這邊都處理清楚了,以後你是你,我是我,我們可以分手了
- 女方收到訊息後,返回告訴男方:我知道了,分手吧
於是乎,它們就斷了,分手手續完成。具體詳細的資訊可看猿人谷的面試官,不要再問我三次握手和四次揮手,一個字:細
傳送HTTP請求
TCP連線已經通了,現在正式傳送 HTTP 請求,這裡又有的聊了,如 HTTP 的報文內容、請求頭、響應頭、請求方法、狀態碼等知識點
首先 HTTP 的報文結構由 起始行 + 頭部 + 空行 + 實體組成,簡單來說就是 header+body,HTTP 的報文可以沒有body(get方法),但必須要有 header
請求頭由請求行 + 頭部欄位構成,響應頭由狀態行 + 頭部欄位構成
請求行有三部分:請求方法、請求目標和版本號
- 例如 GET / HTTP/1.1
狀態行也有三部分:版本號、狀態碼和原因字串
- 例如 HTTP/1.1 200 OK
在瀏覽器中,開啟F12,在 NetWork 中任何一個請求中,你都會看到這樣的結構
這裡我們也常會遇到一些例如 GET 和 POST 請求方式的區別、HTTP 狀態碼等相關的衍生問題
GET 和 POST 請求方式的區別
- 從快取角度看,GET 會被快取,POST 不會被快取
- 從引數角度看,GET 通過在 URL 的"?"後以 key=value 方式傳參,資料之間以“&”相連線;POST 則要將資料封裝到請求體中傳送,這個過程不可見
- 從安全形度看,GET 不安全,因為 URL 可見;POST 較 GET 安全度高
- 從編碼角度看,GET 只接受 ASCII 字元,向伺服器傳送中文字元可能會出現亂碼;POST 支援標準字符集,可以正確傳遞中文
- 從資料長度的限制看,GET 一般受 URL 長度限制(URL 的最大長度是 2048 個字元),POST 無限制
HTTP 狀態碼
RFC 標準把狀態碼分成了五類 ,用數字的第一位表示分類,而 0~99 不用,這樣狀態碼的實際可用範圍就大大縮小了,由 000~999 變成了 100~599。
這五類的具體含義是:
- 1××:提示資訊,表示目前是協議處理的中間狀態,還需要後續的操作;
- 2××:成功,報文已經收到並被正確處理;
- 3××:重定向,資源位置發生變動,需要客戶端重新傳送請求;
- 4××:客戶端錯誤,請求報文有誤,伺服器無法處理;
- 5××:伺服器錯誤,伺服器在處理請求時內部發生了錯誤。
目前 RFC 標準裡總共有 41 個狀態碼
101 - Switching Protocols,客戶端使用 Upgrade 頭欄位
200 - 請求成功
204 - 無內容,伺服器成功處理了請求,但沒有返回任何內容。
206 - 一般用來做斷點續傳,或者是視訊檔案等大檔案的載入
301 - 永久重定向
302 - 臨時重定向
304 - 未修改協商快取,返回快取中的資料。它不具有通常的跳轉含義,但可以理解成 重定向到快取的檔案(即快取重定向)
400 - 請求中語法錯誤
401 - 未授權
403 - 伺服器收到請求,但是拒絕提供服務,即資源不可用
404 - 無法找到請求資源
408 Request Timeout - 請求超時
414 - 請求 URI 過長(如圖一新浪常有)
500 - 伺服器內部錯誤
501 - 尚未實施:伺服器不具備請求功能
502 - 閘道器錯誤
503 - 伺服器不可用,主動用503響應請求或 Nginx 設定限速,超過限速,會返回503
504 - 閘道器超時
這裡要對 304 做一下說明,當請求頭 If-Modified-Since
或 If-None-Match
中判斷修改時間是否一致(或唯一標識是否一致),是,則返回304,使用瀏覽器記憶體中的本地快取;不一致則說明要更新,繼續請求資源放回給客戶端,並帶上 Last-Modified
或 ETag
請求方式
HTTP/1.1 規定了八種方法,都必須是大寫形式
- GET:獲取資源,可以理解為讀取或者下載資料。只有GET請求才能起到快取效果
- HEAD:獲取資源的元資訊
- POST:像資源提交資料,相當於寫入或上傳資料
- PUT:類似 POST
- DELETE:刪除資源
- CONNECT:建立特殊的連線隧道
- OPTIONS:列出可對資源實行的方式
- TRACE:追蹤請求 - 響應的傳輸路徑
瀏覽器渲染
當HTTP 請求完畢後,斷開 TCP 連線,將資源返回給客戶端(瀏覽器)。此時瀏覽器要判斷是否與開啟的網站是同一個站點。因為如果是同一個站點的話,則可使用同站點的渲染程式渲染頁面,如果不是,瀏覽器則開啟新的渲染程式解析資源
瀏覽器渲染的大致流程如下圖所示:
我們可以將頁面渲染分為三個步驟:
解析
- HTML 被解析為 DOM 樹,CSS 被解析為 CSS 規則樹,JavaScript 通過 DOM API 和 CSSOM API 來操作 DOM Tree 和 CSS Rule Tree
渲染
- 瀏覽器引擎通過 DOM Tree 和 CSS Rule Tree 構建 Rendering Tree(渲染樹),這其中進行大量的 迴流(Reflow) 和 重繪(Repaint)
迴流和重繪
- 迴流:意味著元件的幾何尺寸變了,需要重新驗證並計算 Render Tree
- 重繪:螢幕的一部分需要重畫,比如某個CSS 的背景色變了,但元件的幾何尺寸沒有變
- 迴流的成本要比重繪大
繪製
- 最後通過作業系統(瀏覽器)的Native GUI的API繪製
其中,衍生出重繪和迴流的問題,提高效能的方法之一就是減少瀏覽器的渲染時間,其中的一個優化點就是減少重繪和迴流
減少迴流和重繪的方法
- 不要一條條修改 DOM 樣式,與其這樣,不如預定義好CSS的class,然後修改DOM的樣式
把DOM“離線”後修改
- 使用 documentFragment 物件在記憶體裡操作 DOM
- 先把 DOM 給
display:none
(有一次 Reflow),然後你想怎麼改就怎麼改,再把它顯示出來 - clone 一個 DOM 節點到記憶體裡,然後想怎麼改就怎麼改,改完後和線上的那個交換一下
- 不要把 DOM 節點的屬性值放在一個迴圈中當作迴圈的變數,不然這會導致大量地讀寫這個節點的屬性
- 儘可能地修改層級比較低的DOM
- 不要使用 table 佈局
造成迴流的屬性:
width、height、padding、margin、border、position、top、left、bottom、right、float、clear、text-align、vertical-align、line-height、font-weight、font-size、font-family、overflow、white-space
造成重繪的屬性:
color、border-style、border-radius、text-decoration、box-shadow、outline、background
記住一點,迴流是與幾何大小相關,重繪與大小無關
如此,從輸入 url 到看到頁面的整個流程就走完了
總結
這道題能衍生很多問題,從一題可以測試出面試者的HTTP、瀏覽器相關知識。正所謂”鵬怒而飛,其翼若垂天之雲;水擊三千里,碧空九萬丈;好風憑藉力,送我上青雲。“。這道題之所以能成為經典題,不是沒有它的原因的
筆者這裡做一個總結,把這題可以衍生的知識點逐一列出,待君思索
瀏覽器方面
瀏覽器架構
- 由什麼組成?瀏覽器主程式、GPU程式、多個渲染程式、多個外掛程式、網路程式、音訊程式、儲存程式等
- 渲染程式中有哪些程式?GUI渲染執行緒、JS 引擎執行緒、事件觸發執行緒、網路非同步執行緒、定時器執行緒
- 程式和執行緒的區別?程式是應用程式建立的例項,而執行緒依託於程式,它是計算機最小的執行單位
瀏覽器渲染
- 渲染流程?解析、渲染、繪製
重繪和迴流
- 兩者的區別
- 重繪和迴流的屬性
- 如何減少重繪和迴流,提高渲染效能
HTTP方面
HTTP快取
強快取
- HTTP/1.1 Cache-Control
- HTTP/1.0 Expires
- Cache-Control > Expires
協商快取
- HTTP/1.1 ETag/If-None-Match
- HTTP/1.0 Last-Modified/If-Modified-Since
- 精準度:ETag > Last-Modified
- 效能:Last-Modified > ETag
TCP/IP 連線
- 三次握手、四次揮手
網路層面的效能優化
- HTTP/1.1的做法
- HTTP/2 的做法
- HTTP/3 的做法
- 每個階段採用的效能優化是有所不同的