全面解析URL請求到頁面顯示完整過程

網頁加載流程

URL解析

先進行 URL 解析,看看輸入的內容是否符合 URL 規則(解析 URL 提取出協議、域名、端口號,對於一些特殊字符,在傳遞的時候須要進行編碼解碼)。css

  • encodeURIdecodeURI能夠對中文、空格等編碼解碼,適用於 URL 自己
  • encodeURIComponentdecodeURIComponent範圍更廣,會編碼解碼一些特殊字符如 :/?=+@#$,適用於給參數編碼解碼

緩存檢查

URL 符合規則,瀏覽器進程會經過進程通訊將 URL 請求發送給網絡進程,網絡進程會依次查找 Memory CacheDisk Cache中是否有緩存內容,有且沒過時則使用,不然則發送網絡請求。html

DNS 解析

網絡請求第一步就是先進行 DNS 解析,獲取請求域名服務器的 IP 地址。前端

什麼是 DNS 解析,每臺計算機都有一個惟一 IP 地址,可是 IP 地址不方便記憶,因此採用更方便記憶的網址去查找其餘計算機,將網址轉換成 IP 地址的過程就是 DNS 解析。webpack

域名解析是一個遞歸查詢 + 迭代查詢的過程。git

  1. 瀏覽器緩存,向瀏覽器的緩存中讀取上一次的訪問記錄
  2. 操做系統的緩存,查找存儲在系統運行內存中的緩存
  3. host 文件中查找
  4. 路由器緩存:有些路由器會把訪問過的域名存在路由器上
  5. ISP互聯網服務提供商緩存,好比 114.114.114.114
  6. 緩存中找不到,則本地 DNS 服務器進行迭代查詢:.DNS 服務器 -> .com 頂級服務器 -> 主域名服務器 -> ...,直到服務器返回對應的 IP

DNS 負載均衡:github

網站對應的 IP 不止一個,DNS 能夠根據每臺機器的負載量、距離用戶的距離等返回一個合適的服務器 IP 給用戶,這個過程就是 DNS 負載均衡,又叫作 DNS 重定向。 CDN 就是利用 DNS 的重定向技術, DNS 會返回一個用戶最接近的點的 IP 給用戶。web

TCP 鏈接三次握手

拿到 IP 後,(檢查當前域名是否達到 TCP 鏈接上限),經過三次握手進行 TCP 鏈接面試

tcp3.png

三次握手:算法

  1. 第一次:客戶端發送 SYN 包和初始序號 seq = x 給服務端,此時客戶端狀態爲 SYN-SENT
  2. 第二次:服務端收到 SYN 包後,將標識位 SYNACK 置爲1,確認序號 ack = x + 1, 初始序號 seq = y 發送給客戶端,此時服務端狀態爲 SYN-RECEIVED
  3. 第三次:客戶端收到後,將標識位 ACK 置爲1, 確認序號 ack = y + 1, 本身的序號 seq = x + 1, 發送給服務端,服務端收到後也將狀態切換爲 ESTABLISHED

三次握手抽象版:瀏覽器

  1. 客戶端:你是服務端嗎
  2. 服務端:是的,我是服務端,你是客戶端嗎
  3. 客戶端:是的,我是客戶端
  • seq序號,用來標識從TCP源端向目的端發送的字節流,發起方發送數據時對此進行標記
  • ack確認序號,只有ACK標誌位爲1時,確認序號字段纔有效,ack=seq+1

標識位:

  • ACK:確認標識,用於表示對數據包的成功接收。
  • SYN:同步標識,表示 TCP 鏈接已初始化,發起一個新鏈接。
  • FIN:完成標識,釋放一個鏈接,用於拆除上一個 SYN 標識。一個完整的TCP鏈接過程必定會有 SYNFIN 包。

爲何不能兩次握手?

TCP 的特色的可靠傳輸,服務端和客戶端都須要可靠傳輸,就須要確認雙方的發送和接收能力,第一次握手確認了客戶端的發送能力,第二次確認了服務端的發送和接收能力,第三次確認了客戶端的接收能力

兩次握手,服務器不能肯定客戶端已經收到了確認請求,不能確認是否創建好了鏈接。服務器認爲創建好了鏈接,發送數據包,結果發的包客戶端沒收到,那麼攻擊服務器就很容易了,只發包不收包。

TCPUDP 的區別:

  • TCP 是一個面向鏈接的、可靠的、基於字節流的傳輸層協議,TCP 會精準記錄哪些數據發送了,哪些數據被對方接收了,哪些沒有被接收到,並且保證數據包按序到達,不容許半點差錯。這是有狀態, 當意識到丟包了或者網絡環境不佳,TCP 會根據具體狀況調整本身的行爲,控制本身的發送速度或者重發。這是可控制
  • UDP 是一個面向無鏈接的傳輸層協議,無狀態不可控

HTTP請求

TCP 鏈接創建以後,瀏覽器端會構建請求行、 請求頭等信息,並把和該域名相關的 Cookie 等數據附加到請求頭中,而後向服務器發送構建的請求信息。若是是 HTTPS,還須要進行 TSL 協商。

服務器檢查 HTTP 請求頭是否包含緩存驗證信息進行協商緩存

協商緩存

Last-ModifiedIf-Modified-Since

Last_Modified 表示本地文件的最後修改時間,If-Modified-Since 會將 Last-Modified 的值發送給服務器詢問該資源是否有更新,若是有更新就會將新的資源發送回來,不然返回 304 狀態碼,表明資源無更新,繼續使用緩存文件。

Last-Modified 弊端:

  1. 若是文件只是被打開,沒有修改,也會形成 Last-Modified 修改,服務器不能命中緩存。
  2. 只能以秒計時,若是在毫秒級的時間內修改了文件,服務器 Last-Modified 的值並不會修改,會返回304,瀏覽器就會是本身的緩存 。

ETagIf-No-Match

ETag 是文件指紋,If-No-Match 會將 ETag 發送給服務器,查詢該資源 ETag 是否變更,有變更的話就將新的資源發送回來。ETag 優先級高於 Last-Modified

啓發式緩存

若是什麼緩存都沒設置,瀏覽器一般會響應頭中的 Date 減去 Last-Modified 值的 10% 做爲緩存時間。

狀態碼

狀態碼用於表示服務器對請求的處理結果

  • 1xx:指示信息——表示請求已經接受,繼續處理
    • 100 Continue 通常在發送 post 請求時,已發送了 http header 以後服務端返回此信息,表示確認,以後發送具體參數信息。
  • 2xx:成功
    • 200 OK 正常返回信息
    • 201 Created 請求成功而且服務器建立了新的資源
    • 202 Accepted 服務器已接受請求,但還沒有處理
  • 3xx:重定向
    • 301 Moved Permanently 永久重定向
    • 302 Found 臨時重定向
    • 303 See Other 臨時重定向,且老是使用 GET 請求新的 URI
    • 304 Not Modified 請求內容未改動,走緩存
  • 4xx:客戶端錯誤
    • 400 Bad Request 服務器沒法理解請求格式
    • 401 Unauthorized 請求未受權
    • 403 Forbidden 禁止訪問
    • 404 Not Found 找不到與 URI 相匹配的資源
  • 5xx:服務器錯誤。
    • 500 Internal Server Error 服務器內部錯誤
    • 503 Service Unavailable 服務器暫時沒法處理請求

四次揮手

tcp4.png

數據傳輸完後,若是請求頭或響應頭裏沒有 connection: keep-alive,則須要四次揮手斷開 TCP 鏈接,不然會保持鏈接通道,這樣下一次在發送請求,就無需再次TCP三次握手了,節省了網絡通訊時間。

http1.0 中默認 Connection 並非 keep-alive,須要手動處理,可是 HTTP1.1 以後,Connection:keep-alive 已經被列入了規範,如今基本都是默認就是長鏈接,前提是同一個源,向不一樣源發送請求要從新創建通道。

四次揮手:

  • 第一次:客戶端主動關閉放發送一個 FIN,用來關閉客戶端到服務端的數據傳輸,告訴服務端我不會給你發送數據了
  • 第二次:服務端收到 FIN 包後,發送一個 ACK 給客戶端,確認序號爲收到序號 + 1
  • 第三次:服務端發送完數據後,服務端發送一個 FIN,用來關閉服務端到客戶端的數據傳輸,告訴客戶端我不會給你發數據了
  • 第四次:客戶端收到 FIN 後,發送一個 ACK 給服務端,確認序號爲收到序號 + 1,完成四次揮手

四次揮手抽象版:

  1. 客戶端:服務端,我要和你斷開鏈接
  2. 服務端:好的,斷吧
  3. 服務端:我也要和你斷開鏈接
  4. 客戶端:好的,斷吧

四次握手後,客戶端還會等待 2MSL(MSL:最長報文段壽命,通常2min) 的時間,爲了保證客戶端發送的 ACK 報文可以到達服務器,由於這個報文可能會丟失,服務器收不到確認會超時重傳 FIN + ACK 報文段,客戶端能在 2MSL 時間內收到這個重傳的報文段,而後客戶端從新確認。

爲何鏈接的時候是三次握手,關閉的時候倒是四次揮手?

  • 服務端接收到客戶端的 SYN 鏈接請求報文後,能夠直接發送 SYN + ACK 報文
  • 可是關閉鏈接時,當服務端接收到 FIN 報文時,極可能並不會當即關閉鏈接,因此只能先回復一個 ACk 報文,告訴客戶端你發的 FIN 報文我收到了,只有等服務端全部的報文發送完了,我才能發送 FIN 報文,所以不能一塊兒發送,因此須要四次。

客戶端解析資源

  • 瀏覽器拿到資源會根據資源類型進行處理,好比是 gzip 壓縮後的文件則進行解壓縮,若是響應頭 Content-typetext/html,則開始解析 HTML

HTML ParserHTML 文件進行處理,根據 HTML 標記關係構建 DOM 樹。 - 解析過程當中遇到圖片、linkscript會啓動下載。 - script標籤會阻塞 DOM 樹的構建,因此通常將 script 放在底部,或者添加 asyncdefer 標識。 - css 下載時異步,不會阻塞瀏覽器構建 DOM 樹,可是會阻塞渲染,在構建佈局樹時,會等待 css 下載解析完畢後才進行。

  • 渲染引擎將 CSS 樣式錶轉化爲瀏覽器能夠理解的 styleSheets,轉換樣式表中的屬性使其標準化em => px; bold => 700
  • 根據 DOM 樹和 styleSheets 構建佈局樹,計算出元素的佈局信息,display: none不可見節點以及 head 這種不可見標籤不會插入到佈局樹裏
    • 構建 DOM 樹、構建 CSSOM 樹、構建樹並非嚴格的前後順序,爲了讓用戶能儘快看到網頁內容,都是並行推動的
  • 對佈局樹進行分層,生成圖層樹。
    • position: fixed/absolutez-index:2filter: blue(5px)opacity: .5等擁有層疊上下文屬性的元素會進行分層、或者內容須要裁減
  • 繪製圖層須要一個個繪製指令,渲染線程將包含繪製指令的繪製列表提交給合成線程,繪製操做是由合成線程來完成的
  • 合成線程將圖層劃分爲一個個圖塊,優先處理靠近視口的圖塊,對其進行柵格化處理生成位圖
    • 一般,柵格化過程會採用 GPU 加速生成,渲染進程把生成圖塊的指令發送給 GPU 進程,GPU 生成最終的位圖並保存在內存之中
  • 一旦全部圖塊都被光柵化,合成線程向瀏覽器進程提交一個繪製圖塊的命令,將其內容繪製到內存之中,最後顯示在屏幕上

優化

  • 儘早的把 CSS 下載到客戶端,充分利用 HTTP 多請求併發機制,且 CSS 下載並不會阻塞渲染,style、link、@import 放到頁面頂部
  • 避免 JS 加載阻塞渲染,添加 async、defer 標識,標籤放到頁面底部
  • 減小 DOM 的迴流和重繪

asyncdefer 都是異步的,使用 async 標誌的腳本文件一旦加載完成,會當即執行;而使用了 defer 標記的腳本文件,須要在 DOMContentLoaded 事件以前執行。

重繪:元素樣式的改變,可是寬高、大小、位置等不變,好比:colorbackgroundvisibility

迴流:元素的大小或者位置發送了變化,觸發了頁面的從新佈局,甚至調用方法或屬性getComputedStyleclientWidth,爲了保證獲得的結果是即便性準確性,致使佈局樹從新計算佈局和渲染。

優化策略:

  • 減小回流範圍:避免使用 table 佈局,由於一個小改動可能會形成整個 table 的從新佈局
  • 避免逐條改變樣式,使用類名去合併樣式
  • 使用 documentFragment 操做 dom,操做完成後再添加到文檔中
  • 避免頻繁讀取會引起迴流/重繪的屬性,若是確實須要屢次使用,就用一個變量緩存起來。
  • 動畫效果應用到 position 屬性爲 absolutefixed 元素上,脫離文檔流,單獨渲染區域
  • CSS3 硬件加速, transformopacity 等屬性會觸發 GPU 加速,不會引起迴流和重繪,可是過多使用可能會佔用大量內存,性能消耗嚴重
  • 現代瀏覽器會本身緩存一個 flush 隊列,而後一次性清空。

性能優化

DNS 優化

  • DNS 預解析
  • 採用 CDNDNS 負載均衡
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//g.alicdn.com" />  
複製代碼

網絡鏈接優化

  • 分服務器部署,區分 web 服務器、資源服務器、數據服務器,增長 HTTP 併發性
  • 減小 TCP 的三次握手和四次揮手:HTTP1.1默認開啓的 Connection: keep-alive

數據緩存

  • 對於靜態資源文件實現強緩存和協商緩存
  • 對不常常更新的接口數據採用本地存儲作數據緩存,好比地區數據

數據傳輸

  • 減小數據傳輸的大小
  1. 利用工具如 webpack 對傳輸內容進行壓縮
  2. 服務端開發 GZIP 壓縮,通常能壓縮 60% 左右
  3. 大批量數據分批次請求,下拉刷新,分頁
  • 減小 HTTP 請求的次數
  1. 資源文件合併處理
  2. 小圖片轉成 base64,可是可能會形成圖片大小增長 1/3

採用 HTTP2.0

HTTP1.1 雖然在串行請求能夠經過 Connection: keep-alive 複用同一個 TCP 鏈接,若是是並行發送多個請求,會創建多個鏈接,可是瀏覽器通常限制會限制同一域名下最多同時能夠創建6個鏈接。

  • 請求阻塞:在併發請求達最大限制時,請求必須等到上一個請求完成後,才能夠複用這個 TCP 發出下一個請求,因此會受到前面請求的阻塞。
  • 線頭阻塞:請求響應的順序必須和請求發送的順序一致,若是後發送的請求響應完成了,也要等前面的阻塞的請求返回。

多路複用:容許同時經過單一的 HTTP2.0 鏈接發起的多重請求 - 響應消息,鏈接通道是共享的

HTTP2.0 的傳輸是基於二進制幀的,每一個 TCP 鏈接中,都有多個雙向流通的流,每一個流都有獨一無二的標識和優先級,而流就是由二進制幀組成的。二進制幀會標識本身是屬於哪一個流的,因此這些流能夠交錯傳輸,在接收端根據幀頭組裝成完整的信息,解決線頭堵塞的問題。

頭部壓縮:HTTP1.xheader 中帶有大量的信息,每次都要重複發送,HTTP2.0 使用 HPACK 算法對 header 數據進行壓縮,減小須要傳輸的 header 大小,通信雙方各自緩存一個頭部字典表,能夠差別化更新頭部,減小須要傳輸數據的大小

參考文章

相關文章
相關標籤/搜索