1. 程式人生 > >從輸入URL到頁面加載的全過程

從輸入URL到頁面加載的全過程

用戶操作 詳細 單元 事件響應 鏈路層 key-value 80端口 文件 span

前面的話

  本文將詳細介紹從輸入URL到頁面加載的全過程

概述

  從輸入URL到頁面加載的主幹流程如下:

  1、瀏覽器構建HTTP Request請求

  2、網絡傳輸

  3、服務器構建HTTP Response 響應

  4、網絡傳輸

  5、瀏覽器渲染頁面

技術分享圖片

構建請求

  1、應用層進行DNS解析

  通過DNS將域名解析成IP地址。在解析過程中,按照瀏覽器緩存系統緩存路由器緩存ISP(運營商)DNS緩存根域名服務器頂級域名服務器主域名服務器的順序,逐步讀取緩存,直到拿到IP地址

  這裏使用DNS預解析,可以根據瀏覽器定義的規則,提前解析之後可能會用到的域名,使解析結果緩存到系統緩存

中,縮短DNS解析時間,來提高網站的訪問速度

  2、應用層生成HTTP請求報文

  接著,應用層生成針對目標WEB服務器的HTTP請求報文,HTTP請求報文包括起始行、首部和主體部分

  如果訪問的google.com,則起始行可能如下

GET https://www.google.com/ HTTP/1.1

  首部包括域名host、keep-alive、User-Agent、Accept-Encoding、Accept-Language、Cookie等信息,可能如下

Host: www.google.com
Connection: keep-alive
Upgrade
-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 X-Client-Data: CKm1yQEIhbbJAQijtskBCMG2yQEIqZ3KAQioo8oB Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

  首部和主體內容之間有一個回車換行(CRLF),主體內容即要傳輸的內容。如果是get請求,則主體內容為空

  3、傳輸層建立TCP連接

  傳輸層傳輸協議分為UDP和TCP兩種

  UDP是無連接的協議,而TCP是可靠的有連接的協議,主要表現在:接收方會對收到的數據進行確認、發送方會重傳接收方未確認的數據、接收方會將接收到數據按正確的順序重新排序,並刪除重復的數據、提供了控制擁擠的機制

  由於HTTP協議使用的是TCP協議,為了方便通信,將HTTP請求報文按序號分為多個報文段(segment),並對每個報文段進行封裝。使用本地一個大於1024以上的隨機TCP源端口(這裏假設是1030)建立到目的服務器TCP80號端口(HTTPS協議對應的端口號是443)的連接,TCP源端口和目的端口被加入到報文段中,學名叫協議數據單元(Protocol Data Unit, PDU)。因TCP是一個可靠的傳輸控制協議,傳輸層還會加入序列號、確認號、窗口大小、校驗和等參數,共添加20字節的頭部信息

技術分享圖片

  TCP協議是面向連接的,所以它在開始傳輸數據之前需要先建立連接。要建立或初始化一個連接,兩端主機必須同步雙方的初始序號。同步是通過交換連接建立數據分段和初始序號來完成的,在連接建立數據分段中包含一個SYN(同步)的控制位。同步需要雙方都發送自己的初始序號,並且發送確認的ACK。此過程就是三次握手

  第一次握手:主機A發往主機B,主機A的初始序號是X,設置SYN位,未設置ACK位

  第二次握手:主機B發往主機A,主機B的初始序號是Y,確認號(ACK)是X+1,X+1確認號暗示己經收到主機A發往主機B的同步序號。設置SYN位和ACK位

  第三次握手:主機A發往主機B,主機A的序號是X+1,確認號是Y+1,Y+1確認號暗示已經收到主機B發往主機A的同步序號。設置ACK位,未設置SYN位

  三次握手解決的不僅僅有序號問題,還解決了包括窗口大小、MTU(Maximum Transmission Unit,最大傳輸單元),以及所期望的網絡延時等其他問題

技術分享圖片

  構建TCP請求會增加大量的網絡時延,常用的優化方式如下所示

  (1)資源打包,合並請求

  (2)多使用緩存,減少網絡傳輸

  (3)使用keep-alive建立持久連接

  (4)使用多個域名,增加瀏覽器的資源並發加載數,或者使用HTTP2的管道化連接的多路復用技術

  4、網絡層使用IP協議來選擇路線

  處理來自傳輸層的數據段segment,將數據段segment裝入數據包packet,填充包頭,主要就是添加源和目的IP地址,然後發送數據。在數據傳輸的過程中,IP協議負責選擇傳送的路線,稱為路由功能

技術分享圖片

  5、數據鏈路層實現網絡相鄰結點間可靠的數據通信

  為了保證數據的可靠傳輸,把數據包packet封裝成幀(Frame),並按順序傳送各幀。由於物理線路的不可靠,發出的數據幀有可能在線路上出錯或丟失,於是為每個數據分塊計算出CRC(循環冗余檢驗),並把CRC添加到幀中,這樣接收方就可以通過重新計算CRC來判斷數據接收的正確性。一旦出錯就重傳

  將數據包packet封裝成幀(Frame),包括幀頭和幀尾。幀尾是添加被稱做CRC的循環冗余校驗部分。幀頭主要是添加數據鏈路層的地址,即數據鏈路層的源地址和目的地址,即網絡相鄰結點間的源MAC地址和目的MAC地址

  6、物理層傳輸數據

  數據鏈路層的幀(Frame)轉換成二進制形式的比特(Bit)流,從網卡發送出去,再把比特轉換成電子、光學或微波信號在網絡中傳輸

【總結】

  上面的6個步驟可總結為:DNS解析URL地址、生成HTTP請求報文、構建TCP連接、使用IP協議選擇傳輸路線、數據鏈路層保證數據的可靠傳輸、物理層將數據轉換成電子、光學或微波信號進行傳輸

技術分享圖片

網絡傳輸

  從客戶機到服務器需要通過許多網絡設備, 一般地,包括集線器、交換器、路由器等

【集線器】

  集線器是物理層設備,比特流到達集線器後,集線器簡單地對比特流進行放大,從除接收端口以外的所有端口轉發出去

【交換機】

  交換機是數據鏈路層設備,比特流到達交換機,交換機除了對比特流進行放大外,還根據源MAC地址進行學習,根據目的MAC地址進行轉發。交換機根據數據幀中的目的MAC地址査詢MAC地址表,把比特流從對應的端口發送出去

【路由器】

  路由器是網絡層設備,路由器收到比特流,轉換成幀上傳到數據鏈路層,路由器比較數據幀的目的MAC地址,如果有與路由器接收端口相同的MAC地址,則路由器的數據鏈路層把數據幀進行解封裝,然後上傳到路由器的網絡層,路由器找到數據包的目的IP地址,並查詢路由表,將數據從入端口轉發到出端口。接著在網絡層重新封裝成數據包packet,下沈到數據鏈路層重新封裝成幀frame,下沈到物理層,轉換成二進制比特流,發送出去

技術分享圖片

服務器處理及反向傳輸

  服務器接收到這個比特流,把比特流轉換成幀格式,上傳到數據鏈路層,服務器發現數據幀中的目的MAC地址與本網卡的MAC地址相同,服務器拆除數據鏈路層的封裝後,把數據包上傳到網絡層。服務器的網絡層比較數據包中的目的IP地址,發現與本機的IP地址相同,服務器拆除網絡層的封裝後,把數據分段上傳到傳輸層。傳輸層對數據分段進行確認、排序、重組,確保數據傳輸的可靠性。數據最後被傳到服務器的應用層

  HTTP服務器,如nginx通過反向代理,將其定位到服務器實際的端口位置,如8080。比如,8080端口對應的是一個NodeJS服務,生成響應報文,報文主體內容是google首頁的HTML頁面

  接著,通過傳輸層、網絡層、數據鏈路層的層層封裝,最終將響應報文封裝成二進制比特流,並轉換成其他信號,如電信號到網絡中傳輸

  反向傳輸的過程與正向傳輸的過程類似,就不再贅述

瀏覽器渲染

  客戶機接受到二進制比特流之後,把比特流轉換成幀格式,上傳到數據鏈路層,客戶機發現數據幀中的目的MAC地址與本網卡的MAC地址相同,拆除數據鏈路層的封裝後,把數據包上傳到網絡層。網絡層比較數據包中的目的IP地址,發現與本機的IP地址相同,拆除網絡層的封裝後,把數據分段上傳到傳輸層。傳輸層對數據分段進行確認、排序、重組,確保數據傳輸的可靠性。數據最後被傳到應用層

  1、如果HTTP響應報文是301或302重定向,則瀏覽器會相應頭中的location再次發送請求

  2、瀏覽器處理HTTP響應報文中的主體內容,首先使用loader模塊加載相應的資源

  loader模塊有兩條資源加載路徑:主資源加載路徑和派生資源加載路徑。主資源即google主頁的index.html文件 ,派生資源即index.html文件中用到的資源

  主資源到達後,瀏覽器的Parser模塊解析主資源的內容,生成派生資源對應的DOM結構,然後根據需求觸發派生資源的加載流程。比如,在解析過程中,如果遇到img的起始標簽,會創建相應的image元素HTMLImageElement,接著依據img標簽的內容設置HTMLImageElement的屬性。在設置src屬性時,會觸發圖片資源加載,發起加載資源請求

  這裏常見的優化點是對派生資源使用緩存

  3、使用parse模塊解析HTML、CSS、Javascript資源

【解析HTML】

  HTML解析分為可以分為解碼、分詞、解析、建樹四個步驟

  (1)解碼:將網絡上接收到的經過編碼的字節流,解碼成Unicode字符

  (2)分詞:按照一定的切詞規則,將Unicode字符流切成一個個的詞語(Tokens)

  (3)解析:根據詞語的語義,創建相應的節點(Node)

  (4)建樹:將節點關聯到一起,創建DOM樹

【解析CSS】

  頁面中所有的CSS由樣式表CSSStyleSheet集合構成,而CSSStyleSheet是一系列CSSRule的集合,每一條CSSRule則由選擇器CSSStyleSelector部分和聲明CSSStyleDeclaration部分構成,而CSSStyleDeclaration是CSS屬性和值的Key-Value集合

  CSS解析完畢後會進行CSSRule的匹配過程,即尋找滿足每條CSS規則Selector部分的HTML元素,然後將其Declaration聲明部分應用於該元素。實際的規則匹配過程會考慮到默認和繼承的CSS屬性、匹配的效率及規則的優先級等因素

【解析JS】

  JavaScript一般由單獨的腳本引擎解析執行,它的作用通常是動態地改變DOM樹(比如為DOM節點添加事件響應處理函數),即根據時間(timer)或事件(event)映射一棵DOM樹到另一棵DOM樹

  簡單來說,經過了Parser模塊的處理,瀏覽器把頁面文本轉換成了一棵節點帶CSS Style、會響應自定義事件的Styled DOM樹

  4、構建DOM樹、Render樹及RenderLayer樹

  瀏覽器的解析過程就是將字節流形式的網頁內容構建成DOM樹、Render樹及RenderLayer樹的過程

  使用parse解析HTML的過程,已經完成了DOM樹的構建,接下來構建Render樹

【Render樹】

  Render樹用於表示文檔的可視信息,記錄了文檔中每個可視元素的布局及渲染方式

  RenderObject是Render樹所有節點的基類,作用類似於DOM樹的Node類。這個類存儲了繪制頁面可視元素所需要的樣式及布局信息,RenderObject對象及其子類都知道如何繪制自己。事實上繪制Render樹的過程就是RenderObject按照一定順序繪制自身的過程

  DOM樹上的節點與Render樹上的節點並不是一一對應的。只有DOM樹的根節點及可視節點才會創建對應的RenderObject節點

【Render Layer樹】

  Render Layer樹以層為節點組織文檔的可視信息,網頁上的每一層對應一個Render Layer對象。RenderLayer樹可以看作Render樹的稀疏表示,每個RenderLayer樹的節點都對應著一棵Render樹的子樹,這棵子樹上所有Render節點都在網頁的同一層顯示

  RenderLayer樹是基於RenderObject樹構建的,滿足一定條件的RenderObject才會建立對應的RenderLayer節點

  下面是RenderLayer節點的創建條件:

  (1)網頁的root節點

  (2)有顯式的CSS position屬性(relative,absolute,fixed)

  (3)元素設置了transform

  (4)元素是透明的,即opacity不等於1

  (5)節點有溢出(overflow)、alpha mask或者反射(reflection)效果。

  (6)元素有CSS filter(濾鏡)屬性

  (7)2D Canvas或者WebGL

  (8)Video元素

  5、布局和渲染

  布局就是安排和計算頁面中每個元素大小位置等幾何信息的過程。HTML采用流式布局模型,基本的原則是頁面元素在順序遍歷過程中依次按從左至右、從上至下的排列方式確定各自的位置區域

  簡單情況下,布局可以順序遍歷一次Render樹完成,但也有需要叠代的情況。當祖先元素的大小位置依賴於後代元素或者互相依賴時,一次遍歷就無法完成布局,如Table元素的寬高未明確指定而其下某一子元素Tr指定其高度為父Table高度的30%的情況

  Paint模塊負責將Render樹映射成可視的圖形,它會遍歷Render樹調用每個Render節點的繪制方法將其內容顯示在一塊畫布或者位圖上,並最終呈現在瀏覽器應用窗口中成為用戶看到的實際頁面

  主要繪制順序如下:

  (1)背景顏色

  (2)背景圖片

  (3)邊框

  (4)子呈現樹節點

  (5)輪廓

  6、硬件加速

  開啟硬件渲染,即合成加速,會為需要單獨繪制的每一層創建一個GraphicsLayer

  硬件渲染是指網頁各層的合成是通過GPU完成的,它采用分塊渲染的策略,分塊渲染是指:網頁內容被一組Tile覆蓋,每塊Tile對應一個獨立的後端存儲,當網頁內容更新時,只更新內容有變化的Tile。分塊策略可以做到局部更新,渲染效率更高

  一個Render Layer對象如果需要後端存儲,它會創建一個Render Layer Backing對象,該對象負責Renderlayer對象所需要的各種存儲。如果一個Render Layer對象可以創建後端存儲,那麽將該RenderLayer稱為合成層(Compositing Layer)

  如果一個Render Layer對象具有以下的特征之一,那麽它就是合成層:

  (1)RenderLayer具有CSS 3D屬性或者CSS透視效果。

  (2)RenderLayer包含的RenderObject節點表示的是使用硬件加速的視頻解碼技術的HTML5 ”video”元素。

  (3) RenderLayer包含的RenderObject節點表示的是使用硬件加速的Canvas2D元素或者WebGL技術。

  (4)RenderLayer使用了CSS透明效果的動畫或者CSS變換的動畫。

  (5)RenderLayer使用了硬件加速的CSSfilters技術。

  (6)RenderLayer使用了剪裁(clip)或者反射(reflection)屬性,並且它的後代中包括了一個合成層。

  (7)RenderLayer有一個Z坐標比自己小的兄弟節點,該節點是一個合成層

  最終的渲染流程如下所示:

技術分享圖片

【重繪和回流】

  重繪和回流是在頁面渲染過程中非常重要的兩個概念。頁面生成以後,腳本操作、樣式表變更,以及用戶操作都可能觸發重繪和回流

  回流reflow是firefox裏的術語,在chrome中稱為重排relayout

  回流是指窗口尺寸被修改、發生滾動操作,或者元素位置相關屬性被更新時會觸發布局過程,在布局過程中要計算所有元素的位置信息。由於HTML使用的是流式布局,如果頁面中的一個元素的尺寸發生了變化,則其後續的元素位置都要跟著發生變化,也就是重新進行流式布局的過程,所以被稱之為回流

  前面介紹過渲染引擎生成的3個樹:DOM樹、Render樹、Render Layer樹。回流發生在Render樹上。常說的脫離文檔流,就是指脫離渲染樹Render Tree

  重繪是指當與視覺相關的樣式屬性值被更新時會觸發繪制過程,在繪制過程中要重新計算元素的視覺信息,使元素呈現新的外觀

  由於元素的重繪repaint只發生在渲染層 render layer上。所以,如果要改變元素的視覺屬性,最好讓該元素成為一個獨立的渲染層render layer

  下面列舉一些減少回流次數的方法

  (1)不要一條一條地修改DOM樣式,而是修改className或者修改style.cssText

  (2)在內存中多次操作節點,完成後再添加到文檔中去

  (3)對於一個元素進行復雜的操作時,可以先隱藏它,操作完成後再顯示

  (4)在需要經常獲取那些引起瀏覽器回流的屬性值時,要緩存到變量中

  (5)不要使用table布局,因為一個小改動可能會造成整個table重新布局。而且table渲染通常要3倍於同等元素時間

  此外,將需要多次回流的元素獨立為render layer渲染層,如設置absolute,可以較少重繪範圍;對於一些進行動畫的元素,可以進行硬件渲染,從而避免重繪和回流

從輸入URL到頁面加載的全過程