“在瀏覽器里曲掰,從輸入 URL 到頁面展示疾捍,這中間發(fā)生了什么? ”
這是一道經(jīng)典的面試題栏妖,能比較全面地考察應(yīng)聘者知識(shí)的掌握程度乱豆,其中涉及到了網(wǎng)絡(luò)、操作系統(tǒng)吊趾、Web 等一系列的知識(shí)宛裕。所以我在面試應(yīng)聘者時(shí)也必問這道題,但遺憾的是大多數(shù)人只能回答其中部分零散的知識(shí)點(diǎn)论泛,并不能將這些知識(shí)點(diǎn)串聯(lián)成線揩尸,無法系統(tǒng)而又全面地回答這個(gè)問題。
那么今天我們就一起來探索下這個(gè)流程孵奶,下圖是我梳理出的“從輸入 URL 到頁面展示完整流程示意圖”:
從圖中可以看出疲酌,整個(gè)過程需要各個(gè)進(jìn)程之間的配合,所以在開始正式流程之前,我們還是先來快速回顧下瀏覽器進(jìn)程朗恳、渲染進(jìn)程和網(wǎng)絡(luò)進(jìn)程的主要職責(zé)湿颅。
- 瀏覽器進(jìn)程主要負(fù)責(zé)用戶交互、子進(jìn)程管理和文件儲(chǔ)存等功能粥诫。
- 網(wǎng)絡(luò)進(jìn)程是面向渲染進(jìn)程和瀏覽器進(jìn)程等提供網(wǎng)絡(luò)下載功能油航。
- 渲染進(jìn)程的主要職責(zé)是把從網(wǎng)絡(luò)下載的 HTML、JavaScript怀浆、CSS谊囚、圖片等資源解析為可以顯示和交互的頁面。因?yàn)殇秩具M(jìn)程所有的內(nèi)容都是通過網(wǎng)絡(luò)獲取的执赡,會(huì)存在一些惡意代碼利用瀏覽器漏洞對(duì)系統(tǒng)進(jìn)行攻擊镰踏,所以運(yùn)行在渲染進(jìn)程里面的代碼是不被信任的。這也是為什么 Chrome 會(huì)讓渲染進(jìn)程運(yùn)行在安全沙箱里沙合,就是為了保證系統(tǒng)的安全奠伪。
回顧了瀏覽器的進(jìn)程架構(gòu)后,我們?cè)俳Y(jié)合上圖來看下這個(gè)完整的流程首懈,可以看出绊率,整個(gè)流程包含了許多步驟,我把其中幾個(gè)核心的節(jié)點(diǎn)用藍(lán)色背景標(biāo)記出來了究履。這個(gè)過程可以大致描述為如下滤否。
首先,瀏覽器進(jìn)程接收到用戶輸入的 URL 請(qǐng)求最仑,瀏覽器進(jìn)程便將該 URL 轉(zhuǎn)發(fā)給網(wǎng)絡(luò)進(jìn)程藐俺。
然后,在網(wǎng)絡(luò)進(jìn)程中發(fā)起真正的 URL 請(qǐng)求盯仪。
接著網(wǎng)絡(luò)進(jìn)程接收到了響應(yīng)頭數(shù)據(jù)紊搪,便解析響應(yīng)頭數(shù)據(jù)蜜葱,并將數(shù)據(jù)轉(zhuǎn)發(fā)給瀏覽器進(jìn)程全景。
瀏覽器進(jìn)程接收到網(wǎng)絡(luò)進(jìn)程的響應(yīng)頭數(shù)據(jù)之后,發(fā)送“提交導(dǎo)航 (CommitNavigation)”消息到渲染進(jìn)程牵囤;
渲染進(jìn)程接收到“提交導(dǎo)航”的消息之后爸黄,便開始準(zhǔn)備接收 HTML 數(shù)據(jù),接收數(shù)據(jù)的方式是直接和網(wǎng)絡(luò)進(jìn)程建立數(shù)據(jù)管道揭鳞;
最后渲染進(jìn)程會(huì)向?yàn)g覽器進(jìn)程“確認(rèn)提交”炕贵,這是告訴瀏覽器進(jìn)程:“已經(jīng)準(zhǔn)備好接受和解析頁面數(shù)據(jù)了”。
瀏覽器進(jìn)程接收到渲染進(jìn)程“提交文檔”的消息之后野崇,便開始移除之前舊的文檔称开,然后更新瀏覽器進(jìn)程中的頁面狀態(tài)。
這其中,用戶發(fā)出 URL 請(qǐng)求到頁面開始解析的這個(gè)過程鳖轰,就叫做導(dǎo)航清酥。
1. 用戶輸入
當(dāng)用戶在地址欄中輸入一個(gè)查詢關(guān)鍵字時(shí),地址欄會(huì)判斷輸入的關(guān)鍵字是搜索內(nèi)容蕴侣,還是請(qǐng)求的 URL焰轻。
如果是搜索內(nèi)容,地址欄會(huì)使用瀏覽器默認(rèn)的搜索引擎昆雀,來合成新的帶搜索關(guān)鍵字的 URL辱志。
如果判斷輸入內(nèi)容符合 URL 規(guī)則,比如輸入的是
lzugis.cn
狞膘,那么地址欄會(huì)根據(jù)規(guī)則揩懒,把這段內(nèi)容加上協(xié)議,合成為完整的 URL挽封,如https://lzugis.cn
旭从。
當(dāng)用戶輸入關(guān)鍵字并鍵入回車之后,這意味著當(dāng)前頁面即將要被替換成新的頁面场仲,不過在這個(gè)流程繼續(xù)之前和悦,瀏覽器還給了當(dāng)前頁面一次執(zhí)行 beforeunload
事件的機(jī)會(huì),beforeunload
事件允許頁面在退出之前執(zhí)行一些數(shù)據(jù)清理操作渠缕,還可以詢問用戶是否要離開當(dāng)前頁面鸽素,比如當(dāng)前頁面可能有未提交完成的表單等情況,因此用戶可以通過 beforeunload
事件來取消導(dǎo)航亦鳞,讓瀏覽器不再執(zhí)行任何后續(xù)工作馍忽。
當(dāng)前頁面沒有監(jiān)聽 beforeunload
事件或者同意了繼續(xù)后續(xù)流程,那么瀏覽器便進(jìn)入下圖的狀態(tài):
開始加載 URL 瀏覽器狀態(tài)從圖中可以看出燕差,當(dāng)瀏覽器剛開始加載一個(gè)地址之后遭笋,標(biāo)簽頁上的圖標(biāo)便進(jìn)入了加載狀態(tài)。但此時(shí)圖中頁面顯示的依然是之前打開的頁面內(nèi)容徒探,并沒立即替換為極客時(shí)間的頁面瓦呼。因?yàn)樾枰却峤晃臋n階段,頁面內(nèi)容才會(huì)被替換测暗。
2. URL 請(qǐng)求過程
接下來央串,便進(jìn)入了頁面資源請(qǐng)求過程。這時(shí)碗啄,瀏覽器進(jìn)程會(huì)通過進(jìn)程間通信(IPC)把 URL 請(qǐng)求發(fā)送至網(wǎng)絡(luò)進(jìn)程质和,網(wǎng)絡(luò)進(jìn)程接收到 URL 請(qǐng)求后,會(huì)在這里發(fā)起真正的 URL 請(qǐng)求流程稚字。那具體流程是怎樣的呢饲宿?
首先厦酬,網(wǎng)絡(luò)進(jìn)程會(huì)查找本地緩存是否緩存了該資源。如果有緩存資源瘫想,那么直接返回資源給瀏覽器進(jìn)程弃锐;如果在緩存中沒有查找到資源,那么直接進(jìn)入網(wǎng)絡(luò)請(qǐng)求流程殿托。這請(qǐng)求前的第一步是要進(jìn)行 DNS 解析霹菊,以獲取請(qǐng)求域名的服務(wù)器 IP 地址。如果請(qǐng)求協(xié)議是 HTTPS支竹,那么還需要建立 TLS 連接旋廷。
接下來就是利用 IP 地址和服務(wù)器建立 TCP 連接。連接建立之后礼搁,瀏覽器端會(huì)構(gòu)建請(qǐng)求行饶碘、請(qǐng)求頭等信息,并把和該域名相關(guān)的 Cookie 等數(shù)據(jù)附加到請(qǐng)求頭中馒吴,然后向服務(wù)器發(fā)送構(gòu)建的請(qǐng)求信息扎运。
服務(wù)器接收到請(qǐng)求信息后,會(huì)根據(jù)請(qǐng)求信息生成響應(yīng)數(shù)據(jù)(包括響應(yīng)行饮戳、響應(yīng)頭和響應(yīng)體等信息)豪治,并發(fā)給網(wǎng)絡(luò)進(jìn)程。等網(wǎng)絡(luò)進(jìn)程接收了響應(yīng)行和響應(yīng)頭之后扯罐,就開始解析響應(yīng)頭的內(nèi)容了负拟。
(1)重定向
在接收到服務(wù)器返回的響應(yīng)頭后,網(wǎng)絡(luò)進(jìn)程開始解析響應(yīng)頭歹河,如果發(fā)現(xiàn)返回的狀態(tài)碼是 301 或者 302掩浙,那么說明服務(wù)器需要瀏覽器重定向到其他 URL。這時(shí)網(wǎng)絡(luò)進(jìn)程會(huì)從響應(yīng)頭的 Location 字段里面讀取重定向的地址秸歧,然后再發(fā)起新的 HTTP 或者 HTTPS 請(qǐng)求厨姚,一切又重頭開始了。
比如键菱,我們?cè)诮K端里輸入以下命令:
curl -I https://lzugis.cn
curl -I + URL的命令是接收服務(wù)器返回的響應(yīng)頭的信息谬墙。執(zhí)行命令后,我們看到服務(wù)器返回的響應(yīng)頭信息如下:
從圖中可以看出纱耻,服務(wù)器返回的響應(yīng)頭的狀態(tài)碼是 200芭梯,這是告訴瀏覽器一切正常险耀,可以繼續(xù)往下處理該請(qǐng)求了弄喘。
好了,以上是重定向內(nèi)容的介紹∷ξ現(xiàn)在你應(yīng)該理解了蘑志,在導(dǎo)航過程中,如果服務(wù)器響應(yīng)行的狀態(tài)碼包含了 301、302 一類的跳轉(zhuǎn)信息急但,瀏覽器會(huì)跳轉(zhuǎn)到新的地址繼續(xù)導(dǎo)航澎媒;如果響應(yīng)行是 200,那么表示瀏覽器可以繼續(xù)處理該請(qǐng)求波桩。
(2)響應(yīng)數(shù)據(jù)類型處理
在處理了跳轉(zhuǎn)信息之后戒努,我們繼續(xù)導(dǎo)航流程的分析。URL 請(qǐng)求的數(shù)據(jù)類型镐躲,有時(shí)候是一個(gè)下載類型储玫,有時(shí)候是正常的 HTML 頁面,那么瀏覽器是如何區(qū)分它們呢萤皂?
答案是 Content-Type
撒穷。Content-Type
是 HTTP 頭中一個(gè)非常重要的字段, 它告訴瀏覽器服務(wù)器返回的響應(yīng)體數(shù)據(jù)是什么類型裆熙,然后瀏覽器會(huì)根據(jù) Content-Type
的值來決定如何顯示響應(yīng)體的內(nèi)容端礼。這里我們還是以極客時(shí)間為例,看看lzugis官網(wǎng)返回的 Content-Type
值是什么入录。在終端輸入以下命令:
curl -I https://lzugis.cn
我們看到服務(wù)器返回如下信息:
從圖中可以看到蛤奥,響應(yīng)頭中的 Content-type
字段的值是 text/html
,這就是告訴瀏覽器僚稿,服務(wù)器返回的數(shù)據(jù)是 HTML 格式喻括。
3. 準(zhǔn)備渲染進(jìn)程
默認(rèn)情況下,Chrome 會(huì)為每個(gè)頁面分配一個(gè)渲染進(jìn)程贫奠,也就是說唬血,每打開一個(gè)新頁面就會(huì)配套創(chuàng)建一個(gè)新的渲染進(jìn)程。但是唤崭,也有一些例外拷恨,在某些情況下,瀏覽器會(huì)讓多個(gè)頁面直接運(yùn)行在同一個(gè)渲染進(jìn)程中谢肾。
從圖中可以看出腕侄,打開的這三個(gè)頁面都是運(yùn)行在同一個(gè)渲染進(jìn)程中,進(jìn)程 ID 是 80384芦疏。那什么情況下多個(gè)頁面會(huì)同時(shí)運(yùn)行在一個(gè)渲染進(jìn)程中呢冕杠?要解決這個(gè)問題,我們就需要先了解下什么是同一站點(diǎn)(same-site)
酸茴。具體地講分预,我們將“同一站點(diǎn)”定義為根域名(例如,lzugis.cn)加上協(xié)議(例如薪捍,https:// 或者 http://)笼痹,還包含了該根域名下的所有子域名和不同的端口配喳,比如下面這三個(gè):
https://edu.lzugis.cn
https://www.lzugis.cn
https://www.lzugis.cn:8080
它們都是屬于同一站點(diǎn),因?yàn)樗鼈兊膮f(xié)議都是 HTTPS凳干,而且根域名也都是 lzugis.cn晴裹。
Chrome 的默認(rèn)策略是,每個(gè)標(biāo)簽對(duì)應(yīng)一個(gè)渲染進(jìn)程救赐。但如果從一個(gè)頁面打開了另一個(gè)新頁面涧团,而新頁面和當(dāng)前頁面屬于同一站點(diǎn)的話,那么新頁面會(huì)復(fù)用父頁面的渲染進(jìn)程经磅。官方把這個(gè)默認(rèn)策略叫 process-per-site-instance
少欺。
總結(jié)來說,打開一個(gè)新頁面采用的渲染進(jìn)程策略就是:
- 通常情況下馋贤,打開新的頁面都會(huì)使用單獨(dú)的渲染進(jìn)程赞别;
- 如果從 A 頁面打開 B 頁面,且 A 和 B 都屬于同一站點(diǎn)的話配乓,那么 B 頁面復(fù)用 A 頁面的渲染進(jìn)程仿滔;如果是其他情況,瀏覽器進(jìn)程則會(huì)為 B 創(chuàng)建一個(gè)新的渲染進(jìn)程犹芹。
渲染進(jìn)程準(zhǔn)備好之后崎页,還不能立即進(jìn)入文檔解析狀態(tài),因?yàn)榇藭r(shí)的文檔數(shù)據(jù)還在網(wǎng)絡(luò)進(jìn)程中腰埂,并沒有提交給渲染進(jìn)程飒焦,所以下一步就進(jìn)入了提交文檔階段。
4. 提交文檔
所謂提交文檔屿笼,就是指瀏覽器進(jìn)程將網(wǎng)絡(luò)進(jìn)程接收到的 HTML 數(shù)據(jù)提交給渲染進(jìn)程牺荠,具體流程是這樣的:
首先當(dāng)瀏覽器進(jìn)程接收到網(wǎng)絡(luò)進(jìn)程的響應(yīng)頭數(shù)據(jù)之后,便向渲染進(jìn)程發(fā)起“提交文檔”的消息驴一;
渲染進(jìn)程接收到“提交文檔”的消息后休雌,會(huì)和網(wǎng)絡(luò)進(jìn)程建立傳輸數(shù)據(jù)的“管道”;
等文檔數(shù)據(jù)傳輸完成之后肝断,渲染進(jìn)程會(huì)返回“確認(rèn)提交”的消息給瀏覽器進(jìn)程杈曲;
瀏覽器進(jìn)程在收到“確認(rèn)提交”的消息后,會(huì)更新瀏覽器界面狀態(tài)胸懈,包括了安全狀態(tài)担扑、地址欄的 URL、前進(jìn)后退的歷史狀態(tài)趣钱,并更新 Web 頁面涌献。
其中,當(dāng)渲染進(jìn)程確認(rèn)提交之后羔挡,更新內(nèi)容如下圖所示:
這也就解釋了為什么在瀏覽器的地址欄里面輸入了一個(gè)地址后洁奈,之前的頁面沒有立馬消失间唉,而是要加載一會(huì)兒才會(huì)更新頁面绞灼。到這里利术,一個(gè)完整的導(dǎo)航流程就“走”完了,這之后就要進(jìn)入渲染階段了低矮。
5. 渲染階段
一旦文檔被提交印叁,渲染進(jìn)程便開始頁面解析和子資源加載了,一旦頁面生成完成军掂,渲染進(jìn)程會(huì)發(fā)送一個(gè)消息給瀏覽器進(jìn)程轮蜕,瀏覽器接收到消息后,會(huì)停止標(biāo)簽圖標(biāo)上的加載動(dòng)畫蝗锥。
至此跃洛,一個(gè)完整的頁面就生成了。那文章開頭的“從輸入 URL 到頁面展示终议,這中間發(fā)生了什么汇竭?”這個(gè)過程及其“串聯(lián)”的問題也就解決了。