超細(xì)!在瀏覽器輸入xxxhub 回車之后發(fā)生了什么?
點(diǎn)擊上方“碼農(nóng)突圍”,馬上關(guān)注
這里是碼農(nóng)充電第一站,回復(fù)“666”,獲取一份專屬大禮包
真愛,請?jiān)O(shè)置“星標(biāo)”或點(diǎn)個(gè)“在看

# 前言
這個(gè)問題已經(jīng)是老生常談了,更是經(jīng)常被作為面試的壓軸題出現(xiàn),網(wǎng)上也有很多文章,但最近閑的無聊,然后就自己做了一篇筆記,感覺比之前理解更透徹了。
# 大致流程
URL 解析
DNS 查詢
TCP 連接
處理請求
接受響應(yīng)
渲染頁面
一、URL 解析
地址解析:
首先判斷你輸入的是一個(gè)合法的 URL 還是一個(gè)待搜索的關(guān)鍵詞,并且根據(jù)你輸入的內(nèi)容進(jìn)行自動完成、字符編碼等操作。
HSTS
由于安全隱患,會使用 HSTS 強(qiáng)制客戶端使用 HTTPS 訪問頁面。詳見:你所不知道的 HSTS[1]。
其他操作
瀏覽器還會進(jìn)行一些額外的操作,比如安全檢查、訪問限制(之前國產(chǎn)瀏覽器限制 996.icu)。
檢查緩存

二、DNS 查詢
基本步驟

1. 瀏覽器緩存
瀏覽器會先檢查是否在緩存中,沒有則調(diào)用系統(tǒng)庫函數(shù)進(jìn)行查詢。
2. 操作系統(tǒng)緩存
操作系統(tǒng)也有自己的 DNS緩存,但在這之前,會向檢查域名是否存在本地的 Hosts 文件里,沒有則向 DNS 服務(wù)器發(fā)送查詢請求。
3. 路由器緩存
路由器也有自己的緩存。
4. ISP DNS 緩存
ISP DNS 就是在客戶端電腦上設(shè)置的首選 DNS 服務(wù)器,它們在大多數(shù)情況下都會有緩存。
根域名服務(wù)器查詢
在前面所有步驟沒有緩存的情況下,本地 DNS 服務(wù)器會將請求轉(zhuǎn)發(fā)到互聯(lián)網(wǎng)上的根域,下面這個(gè)圖很好的詮釋了整個(gè)流程:

需要注意的點(diǎn)
遞歸方式:一路查下去中間不返回,得到最終結(jié)果才返回信息(瀏覽器到本地DNS服務(wù)器的過程)
迭代方式,就是本地DNS服務(wù)器到根域名服務(wù)器查詢的方式。
什么是 DNS 劫持
前端 dns-prefetch 優(yōu)化
三、TCP 連接
TCP/IP 分為四層,在發(fā)送數(shù)據(jù)時(shí),每層都要對數(shù)據(jù)進(jìn)行封裝:

1. 應(yīng)用層:發(fā)送 HTTP 請求
在前面的步驟我們已經(jīng)得到服務(wù)器的 IP 地址,瀏覽器會開始構(gòu)造一個(gè) HTTP 報(bào)文,其中包括:
請求報(bào)頭(Request Header):請求方法、目標(biāo)地址、遵循的協(xié)議等等
請求主體(其他參數(shù))
其中需要注意的點(diǎn):
瀏覽器只能發(fā)送 GET、POST 方法,而打開網(wǎng)頁使用的是 GET 方法
2. 傳輸層:TCP 傳輸報(bào)文
傳輸層會發(fā)起一條到達(dá)服務(wù)器的 TCP 連接,為了方便傳輸,會對數(shù)據(jù)進(jìn)行分割(以報(bào)文段為單位),并標(biāo)記編號,方便服務(wù)器接受時(shí)能夠準(zhǔn)確地還原報(bào)文信息。
在建立連接前,會先進(jìn)行 TCP 三次握手。
“關(guān)于 TCP/IP 三次握手,網(wǎng)上已經(jīng)有很多段子和圖片生動地描述了。相關(guān)知識點(diǎn):
SYN 泛洪攻擊”
3. 網(wǎng)絡(luò)層:IP協(xié)議查詢Mac地址
將數(shù)據(jù)段打包,并加入源及目標(biāo)的IP地址,并且負(fù)責(zé)尋找傳輸路線。
判斷目標(biāo)地址是否與當(dāng)前地址處于同一網(wǎng)絡(luò)中,是的話直接根據(jù) Mac 地址發(fā)送,否則使用路由表查找下一跳地址,以及使用 ARP 協(xié)議查詢它的 Mac 地址。
“注意:在 OSI 參考模型中 ARP 協(xié)議位于鏈路層,但在 TCP/IP 中,它位于網(wǎng)絡(luò)層?!?/span>
4. 鏈路層:以太網(wǎng)協(xié)議
以太網(wǎng)協(xié)議
根據(jù)以太網(wǎng)協(xié)議將數(shù)據(jù)分為以“幀”為單位的數(shù)據(jù)包,每一幀分為兩個(gè)部分:
標(biāo)頭:數(shù)據(jù)包的發(fā)送者、接受者、數(shù)據(jù)類型
數(shù)據(jù):數(shù)據(jù)包具體內(nèi)容
Mac 地址
以太網(wǎng)規(guī)定了連入網(wǎng)絡(luò)的所有設(shè)備都必須具備“網(wǎng)卡”接口,數(shù)據(jù)包都是從一塊網(wǎng)卡傳遞到另一塊網(wǎng)卡,網(wǎng)卡的地址就是 Mac 地址。每一個(gè) Mac 地址都是獨(dú)一無二的,具備了一對一的能力。
廣播
發(fā)送數(shù)據(jù)的方法很原始,直接把數(shù)據(jù)通過 ARP 協(xié)議,向本網(wǎng)絡(luò)的所有機(jī)器發(fā)送,接收方根據(jù)標(biāo)頭信息與自身 Mac 地址比較,一致就接受,否則丟棄。
注意:接收方回應(yīng)是單播。
“相關(guān)知識點(diǎn):
ARP 攻擊”
服務(wù)器接受請求
接受過程就是把以上步驟逆轉(zhuǎn)過來,參見上圖。
四、服務(wù)器處理請求
大致流程

HTTPD
最常見的 HTTPD 有 Linux 上常用的 Apache 和 Nginx,以及 Windows 上的 IIS。
它會監(jiān)聽得到的請求,然后開啟一個(gè)子進(jìn)程去處理這個(gè)請求。
處理請求
接受 TCP 報(bào)文后,會對連接進(jìn)行處理,對HTTP協(xié)議進(jìn)行解析(請求方法、域名、路徑等),并且進(jìn)行一些驗(yàn)證:
驗(yàn)證是否配置虛擬主機(jī)
驗(yàn)證虛擬主機(jī)是否接受此方法
驗(yàn)證該用戶可以使用該方法(根據(jù) IP 地址、身份信息等)
重定向
假如服務(wù)器配置了 HTTP 重定向,就會返回一個(gè) 301永久重定向響應(yīng),瀏覽器就會根據(jù)響應(yīng),重新發(fā)送 HTTP 請求(重新執(zhí)行上面的過程)
URL 重寫
然后會查看 URL 重寫規(guī)則,如果請求的文件是真實(shí)存在的,比如圖片、html、css、js文件等,則會直接把這個(gè)文件返回。
否則服務(wù)器會按照規(guī)則把請求重寫到 一個(gè) REST 風(fēng)格的 URL 上。
然后根據(jù)動態(tài)語言的腳本,來決定調(diào)用什么類型的動態(tài)文件解釋器來處理這個(gè)請求。
以 PHP 語言的 MVC 框架舉例,它首先會初始化一些環(huán)境的參數(shù),根據(jù) URL 由上到下地去匹配路由,然后讓路由所定義的方法去處理請求。
五、瀏覽器接受響應(yīng)
瀏覽器接收到來自服務(wù)器的響應(yīng)資源后,會對資源進(jìn)行分析。
首先查看 Response header,根據(jù)不同狀態(tài)碼做不同的事(比如上面提到的重定向)。
如果響應(yīng)資源進(jìn)行了壓縮(比如 gzip),還需要進(jìn)行解壓。
然后,對響應(yīng)資源做緩存。
接下來,根據(jù)響應(yīng)資源里的 MIME[3]?類型去解析響應(yīng)內(nèi)容(比如 HTML、Image各有不同的解析方式)。
六、渲染頁面
瀏覽器內(nèi)核

不同的瀏覽器內(nèi)核,渲染過程也不完全相同,但大致流程都差不多。
基本流程

6.1. HTML 解析
首先要知道瀏覽器解析是從上往下一行一行地解析的。
解析的過程可以分為四個(gè)步驟:
① 解碼(encoding)
傳輸回來的其實(shí)都是一些二進(jìn)制字節(jié)數(shù)據(jù),瀏覽器需要根據(jù)文件指定編碼(例如UTF-8)轉(zhuǎn)換成字符串,也就是HTML 代碼。
② 預(yù)解析(pre-parsing)
預(yù)解析做的事情是提前加載資源,減少處理時(shí)間,它會識別一些會請求資源的屬性,比如img標(biāo)簽的src屬性,并將這個(gè)請求加到請求隊(duì)列中。
③ 符號化(Tokenization)
符號化是詞法分析的過程,將輸入解析成符號,HTML 符號包括,開始標(biāo)簽、結(jié)束標(biāo)簽、屬性名和屬性值。
它通過一個(gè)狀態(tài)機(jī)去識別符號的狀態(tài),比如遇到<,>狀態(tài)都會產(chǎn)生變化。
④ 構(gòu)建樹(tree construction)
“注意:符號化和構(gòu)建樹是并行操作的,也就是說只要解析到一個(gè)開始標(biāo)簽,就會創(chuàng)建一個(gè) DOM 節(jié)點(diǎn)?!?/span>
在上一步符號化中,解析器獲得這些標(biāo)記,然后以合適的方法創(chuàng)建DOM對象并把這些符號插入到DOM對象中。
Web page parsing Web page parsing
This is an example Web page.

你從來沒有在瀏覽器看過類似”語法無效”的錯(cuò)誤,這是因?yàn)闉g覽器去糾正錯(cuò)誤的語法,然后繼續(xù)工作。
事件
當(dāng)整個(gè)解析的過程完成以后,瀏覽器會通過DOMContentLoaded事件來通知DOM解析完成。
6.2. CSS 解析
一旦瀏覽器下載了 CSS,CSS 解析器就會處理它遇到的任何 CSS,根據(jù)語法規(guī)范[4]解析出所有的 CSS 并進(jìn)行標(biāo)記化,然后我們得到一個(gè)規(guī)則表。
CSS 匹配規(guī)則
在匹配一個(gè)節(jié)點(diǎn)對應(yīng)的 CSS 規(guī)則時(shí),是按照從右到左的順序的,例如:div p { font-size :14px }會先尋找所有的p標(biāo)簽然后判斷它的父元素是否為div。
所以我們寫 CSS 時(shí),盡量用 id 和 class,千萬不要過度層疊。
6.3. 渲染樹
其實(shí)這就是一個(gè) DOM 樹和 CSS 規(guī)則樹合并的過程。
“注意:渲染樹會忽略那些不需要渲染的節(jié)點(diǎn),比如設(shè)置了display:none的節(jié)點(diǎn)?!?/span>
計(jì)算
通過計(jì)算讓任何尺寸值都減少到三個(gè)可能之一:auto、百分比、px,比如把rem轉(zhuǎn)化為px。
級聯(lián)
瀏覽器需要一種方法來確定哪些樣式才真正需要應(yīng)用到對應(yīng)元素,所以它使用一個(gè)叫做specificity的公式,這個(gè)公式會通過:
標(biāo)簽名、class、id
是否內(nèi)聯(lián)樣式
!important
然后得出一個(gè)權(quán)重值,取最高的那個(gè)。
渲染阻塞
當(dāng)遇到一個(gè)script標(biāo)簽時(shí),DOM 構(gòu)建會被暫停,直至腳本完成執(zhí)行,然后繼續(xù)構(gòu)建 DOM 樹。
但如果 JS 依賴 CSS 樣式,而它還沒有被下載和構(gòu)建時(shí),瀏覽器就會延遲腳本執(zhí)行,直至 CSS Rules 被構(gòu)建。
所有我們知道:
CSS 會阻塞 JS 執(zhí)行
JS 會阻塞后面的 DOM 解析
為了避免這種情況,應(yīng)該以下原則:
CSS 資源排在 JavaScript 資源前面
JS 放在 HTML 最底部,也就是?




