原文地址:banggan.github.io/2019/02/20/…
前言
作為前端開發,我們平常跟瀏覽器打交道的時間也是最多的。在前端面試中,一個較為經典的問題:在瀏覽器地址中輸入url地址,敲回車,發生了什麼?瀏覽器究竟做了什麼?相信很多人腦海裡都會出現個大概的輪廓,可是一到細化具體的過程就答不上來。那麼,作為前端開發者,應該從哪些方面延伸來回答這個問題呢?
瀏覽器工作流程
對於瀏覽器來說,當在位址列輸入url地址,瀏覽器所做的事情就是把一個url變成一個在螢幕上顯示的網頁,大致的過程是這樣的:
從HTTP請求回來 ,產生流式的資料,DOM的構建、CSS計算、渲染、繪製,都是儘可能的流式處理前一步的產出,不需要等待上一步完全接受才開始處理,所以我們在瀏覽網頁的時候,才會逐步出現頁面。- 瀏覽器接受url開啟一個網路請求執行緒
- 瀏覽器發出一個完整的http請求
- 伺服器接收請求到後臺接收請求
- 使用http請求請求頁面
- 把請求回來的html程式碼解析成DOM樹
- CSS的視覺化格式模型解析
- 根據CSS屬性對元素進行渲染,得到記憶體中的點陣圖
- 對點陣圖的合成
- 繪製頁面
瀏覽器接受url開啟網路請求執行緒
第一點主要涉及的是瀏覽器的程式、執行緒模型以及JS的執行機制:
多程式的瀏覽器
大多數瀏覽器是多程式的,有一個主控程式,以及每一個tab頁面都會新開一個程式(某些情況下多個tab會合並程式) 程式可能包括主控程式,外掛程式,GPU,tab頁(瀏覽器核心)等等。
- Browser程式:瀏覽器的主程式(負責協調、主控),只有一個
- 第三方外掛程式:每種型別的外掛對應一個程式,僅當使用該外掛時才建立
- GPU程式:最多一個,用於3D繪製
- 瀏覽器渲染程式(核心):預設每個Tab頁面一個程式,互不影響,控制頁面渲染,指令碼執行,事件處理等(有時候會優化,如多個空白tab會合併成一個程式)
多執行緒的瀏覽器核心
每一個tab頁面可以看作是瀏覽器核心程式,然後這個程式是多執行緒的,它有幾大類子執行緒:
- GUI執行緒
- JS引擎執行緒
- 事件觸發執行緒
- 定時器執行緒
- 網路請求執行緒
開啟網路執行緒發出一個完整的http請求
該部分主要包括:dns查詢、tcp/ip請求構建、五層因特網等內容
DNS查詢
如果輸入的是域名,需要進行dns解析成IP,大致流程:
- 如果瀏覽器有快取,直接使用瀏覽器快取,否則使用本機快取,再沒有的話就是用host
- 如果本地沒有,就向dns域名伺服器查詢(當然,中間可能還會經過路由,也有快取等),查詢到對應的IP。
tcp/ip請求
這裡的tcp/ip請求需要了解3次握手規則建立連線以及斷開連線時的四次揮手,可以參考之前的部落格:blog.csdn.net/bangbanggan…
五層因特網協議
其實這個概念挺難記全的,記不全沒關係,但是要有一個整體概念。 其實就是一個概念:從客戶端發出http請求到伺服器接收,中間會經過一系列的流程。 簡括就是:從應用層的傳送http請求,到傳輸層通過三次握手建立tcp/ip連線,再到網路層的ip定址,再到資料鏈路層的封裝成幀,最後到物理層的利用物理介質傳輸。 當然,服務端的接收就是反過來的步驟。 五層因特爾協議棧其實就是: 1.應用層(dns,http) 2.傳輸層(tcp,udp) 建立tcp連線(三次握手) 3.網路層(IP,ARP) IP定址 4.資料鏈路層(PPP) 5.物理層 OSI七層框架: 物理層、 資料鏈路層、 網路層、 傳輸層、 會話層、 表示層、 應用層。 表示層:主要處理兩個通訊系統中交換資訊的表示方式,包括資料格式交換,資料加密與解密,資料壓縮與終端型別轉換等 會話層:它具體管理不同使用者和程式之間的對話,如控制登陸和登出過程
網路通訊HTTP協議
HTTP協議是基於TCP協議出現的,在TCP的基礎上規定了Request-Response的模型,決定了通訊必須由瀏覽器端發起的,首先來了解下HTTP協議的格式:
HTTP協議格式
HTTP協議大致可以分成以下部分:其中path是請求路徑、version是固定的字串,依次介紹下面的每個部分:
HTTP Method 請求方法
在requestline裡面的方法部分,表示HTTP的操作型別,常見的幾種請求方法如下:
- GET:瀏覽器通過地址訪問頁面均屬於get請求
- POST:常見的表單提交
- HEAD :跟get類似,區別在於只返回請求頭
- PUT:表示新增資源
- DELETE:表示刪除資源
- CONNECT: 多用於HTTPS和WebSocket
- OPTIONS
- TRACE
HTTP Status code狀態碼
常見的狀態碼有以下幾種:
- 1xx:臨時迴應
- 2xx:請求成功,如200
- 3xx:請求目標有變化,如301和302表示臨時和永久重定向,304表示客戶端沒有更新內容
- 4xx;請求錯誤,如403無許可權,404訪問的資源不存在
- 5xx:服務端錯誤,如500服務端錯誤,503服務端暫時錯誤等
在前端開發中,最熟悉的系列無非是大家都喜歡的200請求成功的標誌,在面試中,問得較多的是304快取問題和301、302重定向的問題。
HTTP HEAD(HTTP頭)
HTTP頭可以看做是一個鍵值對,在HTTP標準中,Request Header如下圖:
Response Header如下圖: 在實際的開發中,完整的列表可以參考rfc2616標準。HTTP Request Body
HTTP請求的body主要用於表單的提交,常見的body格式:
- application/json
- application/x-www-form-urlencoded:使用form標籤提交的html請求,預設產生
- multipart/form-data:當有檔案上傳時,使用的格式
HTTPS
HTTPS在HTTP的基礎上增加了兩個作用,一是確定請求的目標服務端身份,二是保證傳輸的資料不會被篡改或者竊聽,該協議使用加密通道來傳輸HTTP內容,所以首先需要與服務端簡歷TLS加密通道。可以在此處檢視詳情:tools.ietf.org/html/rfc281…
HTTP2
HTTP2是HTTP1.1的升級版,有兩大改進:一是支援服務端推送,二是支援TCP連結複用:則使用同一個TCP連結來傳輸多個HTTP請求。詳情見:tools.ietf.org/html/rfc754…
構建DOM樹
當瀏覽器使用HTTP向服務端請求頁面後,那麼如何去解析請求回來的HTML程式碼、構建DOM樹呢?
字元流如何解析成詞
首先瀏覽器讀取獲取的HTML,根據指定的檔案編碼方式如UTF-8轉換為字元流,再將字串轉換為詞Token。那什麼是詞?詞是編譯原理中的最小單元,如標籤開始、屬性、標籤結束、註釋、CDATA節點。Token會標識出當前Token的種類。舉個列子:
<p class="a" >hello</p>
這裡就可以拆分成<p(p標籤的開始)、class="a"(屬性)、>(p標籤的結束)、hello(文字)、</p>
(結束標籤)
構建DOM樹
接下來就是將詞變成DOM樹。在構建DOM樹時,是一邊生成Token一邊消耗Token來生成節點的。
<html>
<head>
<title>Web page parsing</title>
</head>
<body>
<div>
<h1>Web page parsing</h1>
<p>This is an example Web page.</p>
</div>
</body>
</html>
複製程式碼
構建的DOM樹:
構建CSSOM
在構建完DOM樹,當前的物件只包含節點和屬性,沒有任何樣式資訊,那麼瀏覽器是如何給DOM樹新增CSS屬性呢?我們知道瀏覽器是流式的處理整個過程,我們拿到DOM樹構造好的元素,依次去檢查他匹配的規則,再根據規則的優先順序,做覆蓋和調整。
構建渲染樹
當CSSOM樹和DOM樹都得到之後,將兩個樹進行合併就得到了渲染樹:
佈局與繪製
當瀏覽器生成渲染樹以後,就會根據渲染樹來進行佈局。在這個過程中,每一個元素都要要弄清楚各個節點在頁面中的確切位置和大小,把對應的盒變為相應的點陣圖。一個元素可能對應多個盒(如內聯元素,可能被分成多行)每一個盒都對應著一個點陣圖。合成把部分點陣圖合成變成合成層。最終的繪製過程就是把點陣圖合成層繪製到螢幕上。 這裡提一個常見的問題,重繪和迴流?具體的相關知識參考:segmentfault.com/a/119000001…
總結
瀏覽器的工作流程大致就是:
構建DOM樹-構建CSSOM-構建渲染樹-佈局-繪製