前言
這是面試過程中一道高頻考題。
從面試官的角度思考:
- 出現(xiàn)頻繁,可能是因?yàn)槊嬖嚬偻ǔO矚g問一些考察可深可淺的題目
- 很多面試管喜歡根據(jù)我們應(yīng)聘者的考題回答中,甚至我們隨口說到的知識(shí)點(diǎn),繼續(xù)追問
基本回答:
- 瀏覽器解析 URL 獲取協(xié)議,主機(jī),端口, path
- 瀏覽器獲取主機(jī) ip 地址
- 建立 TCP 連接,然后發(fā)送 HTTP 請(qǐng)求
- 服務(wù)器將響應(yīng)報(bào)文通過 TCP 連接發(fā)送回瀏覽器,瀏覽器接收 HTTP 響應(yīng),根據(jù)資源類型決定如何處理(假設(shè)資源為 HTML 文檔)
- 解析 HTML 文檔,構(gòu)件 DOM 樹,下載資源,構(gòu)造 CSSOM 樹,執(zhí)行 js 腳本,最后展現(xiàn)出來給用戶
如果應(yīng)聘者只回答了上述步驟,很多關(guān)鍵步驟(前端應(yīng)該了解的知識(shí)點(diǎn))沒有提及,很有可能達(dá)不到面試官想要的回答效果。
筆者針對(duì)一些關(guān)鍵步驟,具體展開說明。讓這道題成為我們面試考卷中的加分項(xiàng)。
網(wǎng)絡(luò)請(qǐng)求
構(gòu)建請(qǐng)求
瀏覽器會(huì)構(gòu)建請(qǐng)求行:
// 請(qǐng)求方法是 GET,路徑為根路徑,HTTP 協(xié)議版本為 1.1
GET / HTTP/1.1
然后根據(jù) Cache-control 和 Expires 字段,檢查強(qiáng)緩存,如果命中直接使用,否則進(jìn)入下一步。關(guān)于強(qiáng)緩存,如果不清楚可以參考下圖:
DNS 解析
由于我們輸入的是域名,而數(shù)據(jù)包是通過IP地址傳給對(duì)方的。因此我們需要得到域名對(duì)應(yīng)的IP地址。這個(gè)過程需要依賴一個(gè)服務(wù)系統(tǒng),這個(gè)系統(tǒng)將域名和 IP 一一映射,我們將這個(gè)系統(tǒng)就叫做DNS(域名系統(tǒng))。
DNS 協(xié)議提供通過域名查找 IP 地址,或逆向從 IP 地址反查域名的服務(wù)。得到具體 IP 的過程就是DNS解析。
DNS 是一個(gè)網(wǎng)絡(luò)服務(wù)器,我們的域名解析簡(jiǎn)單來說就是在 DNS 上記錄一條信息記錄。
例如 baidu.com 220.114.23.56(服務(wù)器外網(wǎng)IP地址)80(服務(wù)器端口號(hào))
瀏覽器通過域名去查詢 URL 對(duì)應(yīng)的 IP :
- 瀏覽器緩存:瀏覽器會(huì)按照一定的頻率緩存 DNS 記錄
- 操作系統(tǒng)緩存:如果瀏覽器緩存中找不到需要的 DNS 記錄,那就去操作系統(tǒng)中找
- 路由緩存:路由器也有 DNS 緩存
- ISP 的 DNS 服務(wù)器:ISP 是互聯(lián)網(wǎng)服務(wù)提供商( Internet Service Provider )的簡(jiǎn)稱,ISP 有專門的 DNS 服務(wù)器應(yīng)對(duì) DNS 查詢請(qǐng)求
- 根服務(wù)器:ISP 的 DNS 服務(wù)器還找不到的話,它就會(huì)向根服務(wù)器發(fā)出請(qǐng)求,進(jìn)行遞歸查詢(DNS 服務(wù)器先問根域名服務(wù)器 .com 域名服務(wù)器的 IP 地址,然后再問 .baidu 域名服務(wù)器,依次類推)
建立 TCP 連接
TCP 三次握手的過程如下:
- 客戶端發(fā)送一個(gè)帶 SYN=1,Seq=X 的數(shù)據(jù)包到服務(wù)器端口(第一次握手,由瀏覽器發(fā)起,告訴服務(wù)器我要發(fā)送請(qǐng)求了)
- 服務(wù)器發(fā)回一個(gè)帶 SYN=1, ACK=X+1, Seq=Y 的響應(yīng)包以示傳達(dá)確認(rèn)信息(第二次握手,由服務(wù)器發(fā)起,告訴瀏覽器我準(zhǔn)備接受了,你趕緊發(fā)送吧)
- 客戶端再回傳一個(gè)帶 ACK=Y+1, Seq=Z 的數(shù)據(jù)包,代表“握手結(jié)束”(第三次握手,由瀏覽器發(fā)送,告訴服務(wù)器,我馬上就發(fā)了,準(zhǔn)備接受吧)
謝希仁著《計(jì)算機(jī)網(wǎng)絡(luò)》中講“三次握手”的目的是“為了防止已失效的連接請(qǐng)求報(bào)文段突然又傳送到了服務(wù)端,因而產(chǎn)生錯(cuò)誤”。
發(fā)送 HTTP 請(qǐng)求
現(xiàn)在 TCP 連接建立完畢,瀏覽器可以和服務(wù)器開始通信,即開始發(fā)送 HTTP 請(qǐng)求。瀏覽器發(fā) HTTP 請(qǐng)求要攜帶三樣?xùn)|西:請(qǐng)求行、請(qǐng)求頭和請(qǐng)求體。
[圖片上傳失敗...(image-f9c53-1587981019451)]
1.請(qǐng)求行包含請(qǐng)求方法、URL、協(xié)議版本
- 請(qǐng)求方法包含 8 種:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE
- URL 即請(qǐng)求地址,由 <協(xié)議>://<主機(jī)>:<端口>/<路徑>?<參數(shù)> 組成
- 協(xié)議版本即 http 版本號(hào)
POST /user.html HTTP/1.1
2.請(qǐng)求頭包含請(qǐng)求的附加信息,由關(guān)鍵字/值對(duì)組成,如下
// 服務(wù)器可以接受的文件格式
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng;q=0.8,application/signed-exchange;v=b3
// 指定瀏覽器可以支持的 Web 服務(wù)器返回的內(nèi)容壓縮編碼類型
Accept-Encoding: gzip, deflate, br
// 瀏覽器支持的語言
Accept-Language: zh-CN,zh;q=0.9
// 緩存機(jī)制
Cache-Control: no-cache
// 是否需要持久連接
Connection: keep-alive
// 發(fā)送該請(qǐng)求域名下所有 Cookie 值到服務(wù)器
Cookie: /* 省略cookie信息 */
// 指定請(qǐng)求的服務(wù)器的域名和端口號(hào)
Host: www.baidu.com
Pragma: no-cache
Upgrade-Insecure-Requests: 1
// 用戶代理 UA,包含發(fā)出請(qǐng)求的用戶信息
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1
3.請(qǐng)求體,可以承載多個(gè)請(qǐng)求參數(shù)的數(shù)據(jù),包含回車符、換行符和請(qǐng)求數(shù)據(jù),一般在 POST 方法下存在。
網(wǎng)絡(luò)響應(yīng)
跟請(qǐng)求部分類似,網(wǎng)絡(luò)響應(yīng)具有三個(gè)部分:響應(yīng)行、響應(yīng)頭和響應(yīng)體。
1.響應(yīng)行包含:協(xié)議版本,狀態(tài)碼,狀態(tài)碼描述
HTTP/1.1 200 OK
狀態(tài)碼規(guī)則如下:
- 1xx:指示信息--表示請(qǐng)求已接收,繼續(xù)處理
- 2xx:成功--表示請(qǐng)求已被成功接收、理解、接受
- 3xx:重定向--要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的操作
- 4xx:客戶端錯(cuò)誤--請(qǐng)求有語法錯(cuò)誤或請(qǐng)求無法實(shí)現(xiàn)
- 5xx:服務(wù)器端錯(cuò)誤--服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求
2.響應(yīng)頭部包含響應(yīng)報(bào)文的附加信息,由 名/值 對(duì)組成,如下:
// 緩存機(jī)制
Cache-Control: no-cache
Connection: keep-alive
Content-Encoding: gzip
// 表示具體請(qǐng)求中的媒體類型信息,決定瀏覽器將以什么形式、什么編碼讀取這個(gè)文件
Content-Type: text/html;charset=utf-8
// 原始服務(wù)器消息發(fā)出的時(shí)間
Date: Wed, 04 Dec 2019 12:29:13 GMT
// Web 服務(wù)器軟件名稱
Server: apache
// 由服務(wù)器端向客戶端發(fā)送 cookie
Set-Cookie: rsv_i=f9a0SIItKqzv7kqgAAgphbGyRts3RwTg%2FLyU3Y5Eh5LwyfOOrAsvdezbay0QqkDqFZ0DfQXby4wXKT8Au8O7ZT9UuMsBq2k; path=/; domain=.baidu.com
這里注意下 Set-Cookie 中關(guān)于網(wǎng)絡(luò)安全方面的兩個(gè)值:HttpOnly、SameSite
設(shè)置了 HttpOnly 屬性的 cookie 不能使用 JavaScript 經(jīng)由 Document.cookie 屬性、XMLHttpRequest 和 Request APIs 進(jìn)行訪問,以防范跨站腳本攻擊(XSS)。
SameSite=Lax 允許服務(wù)器設(shè)定一則 cookie 不隨著跨域請(qǐng)求一起發(fā)送,這樣可以在一定程度上防范跨站請(qǐng)求偽造攻擊(CSRF)。
響應(yīng)完成之后要判斷 Connection 字段,如果請(qǐng)求頭或響應(yīng)頭中包含 Connection: Keep-Alive ,表示建立了持久連接,這樣 TCP 連接會(huì)一直保持,之后請(qǐng)求統(tǒng)一站點(diǎn)的資源會(huì)復(fù)用這個(gè)連接。
否則斷開 TCP 連接, 請(qǐng)求-響應(yīng)流程結(jié)束。
3.響應(yīng)主體包含回車符、換行符和響應(yīng)返回?cái)?shù)據(jù),并不是所有響應(yīng)報(bào)文都有響應(yīng)數(shù)據(jù)
總結(jié)瀏覽器端的網(wǎng)絡(luò)請(qǐng)求過程:
瀏覽器解析渲染頁面
瀏覽器解析渲染頁面分為以下五個(gè)步驟:
- 根據(jù) HTML 解析出 DOM 樹
- 根據(jù) CSS 解析生成 CSS 規(guī)則樹
- 結(jié)合 DOM 樹和 CSS 規(guī)則樹,生成渲染樹
- 根據(jù)渲染樹計(jì)算每一個(gè)節(jié)點(diǎn)的信息
- 根據(jù)計(jì)算好的信息繪制頁面
回流時(shí),以上流程會(huì)重新走一遍。重繪時(shí),會(huì)重新計(jì)算樣式,跳過中間步驟直接生成繪制列表??梢姡乩L不一定導(dǎo)致回流,但回流一定發(fā)生了重繪。
構(gòu)建 DOM 樹
-
HTML語法定義
HTML的詞匯與句法定義在w3c組織創(chuàng)建的規(guī)范中。當(dāng)前版本是HTML4,HTML5的工作正在進(jìn)行中。
-
不是上下文無關(guān)語法
在對(duì)解析器的介紹中看到,語法可以用類似 BNF 的格式規(guī)范地定義。不幸的是所有常規(guī)解析器的討論都不適用于 HTML (我提及它們并不是為了娛樂,它們可以用于解析 CSS 和 JavaScript )。HTML 無法用解析器所需的上下文無關(guān)的語法來定義。過去 HTML 格式規(guī)范由 DTD (Document Type Definition) 來定義,但它不是一個(gè)上下文無關(guān)語法。
HTML 與 XML 相當(dāng)接近。XML 有許多可用的解析器。HTML 還有一個(gè) XML 變種叫 XHTML ,那么它們主要區(qū)別在哪里呢?區(qū)別在于 HTML 應(yīng)用更加”寬容”,它容許你漏掉一些開始或結(jié)束標(biāo)簽等。它整個(gè)是一個(gè)“軟”句法,不像 XML 那樣嚴(yán)格死板。 總的來說這一看似細(xì)微的差別造成了兩個(gè)不同的世界。一方面這使得 HTML 很流行,因?yàn)樗菽愕腻e(cuò)誤,使網(wǎng)頁作者的生活變得輕松。另一方面,它使編寫語法格式變得困難。所以綜合來說,HTML 解析并不簡(jiǎn)單,現(xiàn)成的上下文相關(guān)解析器搞不定,XML 解析器也不行。
-
解析算法
- 標(biāo)記化
- 建樹
對(duì)應(yīng)的兩個(gè)過程就是分詞和語法分析(參考Babel 編譯的解析過程)。
這里舉例重點(diǎn)介紹下
HTML5的容錯(cuò)機(jī)制:-
使用
</br>而不是<br>if (t->isCloseTag(brTag) && m_document->inCompatMode()) { reportError(MalformedBRError); t->beginTag = true; }全部換為
<br>的形式。 -
表格離散
<table> <table> <tr><td>inner table</td></tr> </table> <tr><td>outer table</td></tr> </table>WebKit 會(huì)自動(dòng)轉(zhuǎn)換為:
<table> <tr><td>outer table</td></tr> </table> <table> <tr><td>inner table</td></tr> </table> -
表單元素嵌套
這時(shí)候直接忽略里面的
form。
樣式計(jì)算
CSS 樣式來源一般為三種:
- link 標(biāo)簽引用
- style 標(biāo)簽中樣式
- 元素內(nèi)嵌 style 屬性
格式化樣式表
瀏覽器無法直接識(shí)別 CSS 樣式文本,這里渲染引擎接收到 CSS 文本之后將其轉(zhuǎn)化為一個(gè)結(jié)構(gòu)話的對(duì)象,即 styleSheets 。
可以在瀏覽器控制臺(tái)輸入 document.styleSheets 來查看這個(gè)最終結(jié)構(gòu)(包含上述三種 CSS 來源)。
標(biāo)準(zhǔn)化樣式屬性
有一些渲染引擎不容易直接理解的 CSS 樣式數(shù)值,需要在計(jì)算樣式之前將它們標(biāo)準(zhǔn)化。如:em -> px,red -> #ff0000,bold -> 700 等等。
計(jì)算每個(gè)節(jié)點(diǎn)的具體樣式
計(jì)算具體樣式主要遵循兩個(gè)規(guī)則:繼承和層疊
-
繼承:
每個(gè)子節(jié)點(diǎn)都會(huì)默認(rèn)繼承父節(jié)點(diǎn)的樣式屬性,如果父節(jié)點(diǎn)中沒有找到,就采用瀏覽器默認(rèn)樣式,也叫
UserAgent樣式。 -
層疊:
CSS 的層疊性體現(xiàn)在,最終的樣式取決與各個(gè)屬性共同作用的結(jié)果。
計(jì)算完樣式之后,所有樣式值會(huì)被掛載到 window.getComputedStyle 中,也就是可以通過 JS 獲取計(jì)算后的樣式。
生成布局樹
布局樹生成主要分兩部:
- 遍歷生成的 DOM 樹節(jié)點(diǎn),并把它們添加到布局樹中
- 計(jì)算布局樹節(jié)點(diǎn)的坐標(biāo)位置
布局樹只包含可見元素,對(duì)于 head 標(biāo)簽和設(shè)置了 display: none 的元素將不會(huì)被放入其中。
如果想了解布局的細(xì)節(jié),可以讀一讀人人 FED 團(tuán)隊(duì)的文章從Chrome源碼看瀏覽器如何layout布局。
構(gòu)建圖層樹
這里分兩種情況,一種是顯式合成,一種是隱式合成。
顯式合成
一、擁有層疊上下文的節(jié)點(diǎn)
層疊上下文也基本上是有一些特定的 CSS 屬性創(chuàng)建的,一般有以下情況:
HTML 根元素本身就具有層疊上下文
普通元素設(shè)置 position 不為 static 并且設(shè)置了 z-index 屬性,會(huì)產(chǎn)生層疊上下文
元素的 opacity 值不是 1
元素的 transform 值不是 none
元素的 filter 值不是 none
元素的 isolation 值是 isolate
will-change 指定的屬性值為上面任意一個(gè)
二、需要剪裁的地方
比如一個(gè) div,你只給他設(shè)置 100 * 100 像素的大小,而你在里面放了非常多的文字,那么超出的文字部分就需要被剪裁。當(dāng)然如果出現(xiàn)了滾動(dòng)條,那么滾動(dòng)條會(huì)被單獨(dú)提升為一個(gè)圖層。
隱式合成
簡(jiǎn)單說就是層疊等級(jí)低的節(jié)點(diǎn)被提升為單獨(dú)的圖層之后,那么所有層疊等級(jí)比它高的節(jié)點(diǎn)都會(huì)成為一個(gè)單獨(dú)的圖層。
這個(gè)隱式合成其實(shí)隱藏著巨大風(fēng)險(xiǎn),如果在一個(gè)大型應(yīng)用中,當(dāng)一個(gè) z-index 比較低的元素被提升為單獨(dú)圖層之后,層疊在它上面的元素統(tǒng)統(tǒng)會(huì)被提升為單獨(dú)的圖層,可能會(huì)增加上千個(gè)圖層,大大增加內(nèi)存壓力,甚至直接讓頁面崩潰。這就是層爆炸的原理
當(dāng)需要 repaint 時(shí),只需要 repaint 本身,而不會(huì)影響到其他層。
生成繪制列表
渲染引擎會(huì)將圖層的繪制拆分成一個(gè)個(gè)繪制指令,比如先畫背景、再描繪邊框......然后將這些指令按順序組合成一個(gè)待繪制列表。
大家可以 F12 打開 Chrome 開發(fā)者工具,在設(shè)置欄展開 more tools ,然后選擇 Layers 面板,就能看到繪制列表了。
后面就是渲染進(jìn)程的主線程把繪制列表提交給合成線程。然后合成線程選擇視口附近的圖塊,把它交給柵格化線程池生成位圖。
柵格化操作完成后,合成線程會(huì)生成一個(gè)繪制指令 DrawQuad,并發(fā)送給瀏覽器進(jìn)程。瀏覽器進(jìn)程中的 viz 組件 接收到命令,把頁面內(nèi)容繪制到內(nèi)存,也就是生成了頁面。
斷開連接
當(dāng)數(shù)據(jù)傳送完畢,需要斷開 TCP 連接,此時(shí)發(fā)起四次揮手。
- 發(fā)起方往被動(dòng)方發(fā)送報(bào)文,F(xiàn)in、Ack、Seq,表示已經(jīng)沒有數(shù)據(jù)傳輸了。并進(jìn)入 FIN_WAIT_1 狀態(tài)。(請(qǐng)求報(bào)文發(fā)送完成)
- 被動(dòng)方發(fā)送報(bào)文,Ack、Seq,表示同意關(guān)閉請(qǐng)求。此時(shí)主機(jī)發(fā)起方進(jìn)入 FIN_WAIT_2 狀態(tài)。(請(qǐng)求報(bào)文接受完成)
- 被動(dòng)方向發(fā)起方發(fā)送報(bào)文段,F(xiàn)in、Ack、Seq,請(qǐng)求關(guān)閉連接。并進(jìn)入 LAST_ACK 狀態(tài)。(響應(yīng)報(bào)文發(fā)送完成)
- 發(fā)起方向被動(dòng)方發(fā)送報(bào)文段,Ack、Seq。然后進(jìn)入等待 TIME_WAIT 狀態(tài)。被動(dòng)方收到發(fā)起方的報(bào)文段以后關(guān)閉連接。發(fā)起方等待一定時(shí)間未收到回復(fù),則正常關(guān)閉。(響應(yīng)報(bào)文接受完成)
參考文章
感謝
如果本文對(duì)你有幫助,就點(diǎn)個(gè)贊支持下吧!感謝閱讀。