我們以在Firefox瀏覽器中輸入zhihu.com網(wǎng)址為例深入解剖這個過程共啃。首先,為火狐瀏覽器添加一個插件Firebug后重啟概漱,按F12鍵得一控制臺丑慎,輸入地址zhihu.com,點擊其中的網(wǎng)絡(luò)欄發(fā)現(xiàn)從輸入網(wǎng)址到頁面加載完畢共用時間4.2s,如圖1所示竿裂。那么這4.2s都發(fā)生了些什么玉吁?
1.瀏覽器發(fā)起DNS查詢請求
在廣域網(wǎng)中,我們是基于IP地址進行通信的腻异,IP是形如 w.x.y.z的數(shù)字进副,四個數(shù)字都是0~255(比如192.168.0.1),也就是四個字節(jié)悔常。在此影斑,我們訪問的是網(wǎng)址zhihu.com,為此机打,我們需要利用域名服務(wù)系統(tǒng)得到知乎網(wǎng)對應(yīng)的IP地址矫户。DNS查找過程如下:
* 瀏覽器緩存 – 瀏覽器會緩存DNS記錄一段時間。 火狐的DNS緩存時間一般為一分鐘残邀。
* 系統(tǒng)緩存 – 如果在瀏覽器緩存里沒有找到需要的記錄皆辽,瀏覽器會做一個系統(tǒng)調(diào)用。這樣便可獲得系統(tǒng)緩存中的記錄罐旗。
* 路由器緩存 – 系統(tǒng)緩存中若仍沒有記錄膳汪,查詢請求將發(fā)向路由器,它一般會有自己的DNS緩存九秀。
* ISP DNS 緩存 – 最后要檢查的是ISP緩存DNS的服務(wù)器遗嗽。在這一般都能找到相應(yīng)的緩存記錄。
* 遞歸搜索 – ISP的DNS服務(wù)器從跟域名服務(wù)器開始進行遞歸搜索鼓蜒,從.com頂級域名服務(wù)器到域名服務(wù)器痹换。一般DNS服務(wù)器的緩存中會 有.com域名服務(wù)器中的域名,所以到頂級服務(wù)器的匹配過程不是那么必要了都弹。域名服務(wù)器向客戶端返回查詢結(jié)果域名娇豫,從而完成域名到IP地址的轉(zhuǎn)換。
由圖2分析各事件的域名解析時間我們會發(fā)現(xiàn)打開知乎花費在域名解析上的時間大概為17ms畅厢。
2.連接服務(wù)器
在客戶端獲得知乎網(wǎng)對應(yīng)的IP地址后冯痢,我們將采用TCP/IP的 三次握手協(xié)議與服務(wù)器建立連接,首先客戶端將轉(zhuǎn)換得到的IP地址發(fā)送一個連接建立的請求給服務(wù)器框杜,服務(wù)器接收到請求返回一個確認浦楣,客戶端得到確認后再次發(fā)送確認,由此連接建立成功咪辱。如果已經(jīng)有了被保持的連接振劳,則復(fù)用此連接。
分析各事件的建立連接時間我們會發(fā)現(xiàn)打開知乎花費在建立連接上的時間大概為35ms油狂。
3.客戶端向web服務(wù)器發(fā)送HTTP請求
建立連接以后历恐,客戶端便可以向有著知乎IP的web服務(wù)器發(fā)送HTTP請求了寸癌。HTTP請求是一個基于TCP協(xié)議之上的應(yīng)用層協(xié)議——超文本傳輸協(xié)議。請求信息包含一個響應(yīng)頭信息和一個請求頭信息弱贼,一般的web技術(shù)都會把請求進行封裝然后交給服務(wù)器進行處理蒸苇,比如servlet會把請求封裝成httpservletrequest對象,把響應(yīng)封裝成httpsevletresponse對象哮洽。Node.js(簡單來說就是運行在服務(wù)端的JavaScript)的http模塊填渠,當(dāng)你創(chuàng)建服務(wù)器的時候會寫一個回調(diào)函數(shù),回調(diào)的參數(shù)用來接受http請求對象和響應(yīng)對象鸟辅,然后在回調(diào)函數(shù)中對請求進行處理氛什。
GET 這個請求定義了要讀取的URL。如圖3所示匪凉,請求頭信息主要包括了 瀏覽器自身定義 (User-Agent 頭)枪眉,和它希望接受什么類型的相應(yīng) (Accept and Accept-Encoding 頭)。Connection頭要求服務(wù)器為了后邊的請求不要關(guān)閉TCP連接再层。請求頭信息中也包含瀏覽器存儲的該域名的cookies贸铜。在不同頁面請求當(dāng)中,cookies是與跟蹤一個網(wǎng)站狀態(tài)相匹配的鍵值聂受。這樣cookies會存儲登錄用戶名蒿秦,服務(wù)器分配的密碼和一些用戶設(shè)置等。Cookies會以文本文檔形式存儲在客戶機里蛋济,每次請求時發(fā)送給服務(wù)器棍鳖。
一般接觸到的響應(yīng)頭信息就是設(shè)置content-type(內(nèi)容類型,用于定義網(wǎng)絡(luò)文件的類型和網(wǎng)頁的編碼碗旅,決定瀏覽器將以什么形式渡处、什么編碼讀取這個文件)來決定MIME類型(服務(wù)器要將發(fā)送的多媒體數(shù)據(jù)類型告訴瀏覽器,而通知手段就是說明該多媒體數(shù)據(jù)的MIME類型祟辟,從而讓瀏覽器知道接收到的信息哪些是MP3文件医瘫,哪些是Shockwave文件等。)旧困,設(shè)置Cache-Control醇份,last-modify等緩存內(nèi)容。一般來說返回給客戶端的內(nèi)容是一個html字符串吼具,如上圖所示被芳,content-type被設(shè)定為text/html。當(dāng)然也可能客戶端請求的是一個image文件馍悟,那么就是讀取image文件后,content-type可能設(shè)為image/png,image/jpg等剩晴,如圖5中打開知乎網(wǎng)的六個圖片請求然后把內(nèi)容返回給客戶端锣咒。這樣一次對請求的處理就結(jié)束了侵状。
圖6中紫色部分代表等待響應(yīng)的時間,由此可發(fā)現(xiàn)所有文件發(fā)送請求并得到響應(yīng)這個過程花費了將近2.32s毅整。
4.客戶端渲染
客戶端接收到服務(wù)器傳來的響應(yīng)對象趣兄,從中得到html字符串和MIME,根據(jù)MIME知道了要用頁面渲染引擎來處理內(nèi)容即html字符串悼嫉,于是進入頁面渲染階段艇潭,這是一個很龐雜的體系。從瀏覽器的角度講戏蔑,瀏覽器包含幾大組件蹋凝,網(wǎng)絡(luò)功能是其中之一,渲染引擎也是其中之一总棵,還有其它的一些比如自己UI界面鳍寂,javascript解釋器,客戶端數(shù)據(jù)存儲等等情龄,在這里我們主要關(guān)注渲染引擎和javascript解釋器迄汛。我們能夠在瀏覽器中看到一個頁面,那么這個頁面是怎么出現(xiàn)的呢骤视?實際上就是調(diào)用底層繪圖API給畫出來的鞍爱。不同的渲染引擎,F(xiàn)irefox的渲染引擎為Gecko专酗。
整體上頁面渲染的過程大致是這樣的:
渲染引擎得到HTML字符串作為輸入睹逃,然后對HTML進行轉(zhuǎn)換,轉(zhuǎn)化成能夠被DOM處理的形式笼裳。DOM唯卖,文檔對象模型,DOM可以以一種獨立于平臺和語言的方式訪問和修改一個文檔的內(nèi)容和結(jié)構(gòu)躬柬,是表示和處理一個HTML文檔的常用方法拜轨。此外,DOM的設(shè)計是以對象管理組織(OMG)的規(guī)約為基礎(chǔ)的允青,因此可以用于任何編程語言橄碾。最初人們把它認為是一種讓JavaScript在瀏覽器間可移植的方法,不過DOM的應(yīng)用已經(jīng)遠遠超出這個范圍颠锉。DOM技術(shù)使得用戶頁面可以動態(tài)地變化法牲,如可以動態(tài)地顯示或隱藏一個元素,改變它們的屬性琼掠,增加一個元素等拒垃,DOM技術(shù)使得頁面的交互性大大地增強。在HTML轉(zhuǎn)換成能被DOM處理的形式后瓷蛙,接著轉(zhuǎn)換成一個DOM樹悼瓮,在解析html的過程戈毒,解析到<link>,<script>,<img>等一些請求標簽時,會發(fā)送請求把對應(yīng)的內(nèi)容獲取到横堡。這時又會同步進行CSS的解析埋市,計算出最終的樣式數(shù)據(jù),對CSS代碼中非法的語法她會直接忽略掉命贴。解析CSS的時候會按照如下順序來定義優(yōu)先級:瀏覽器默認設(shè)置道宅,用戶設(shè)置,外鏈樣式胸蛛,內(nèi)聯(lián)樣式污茵,HTML中的style。構(gòu)建出CSS樣式規(guī)則應(yīng)用到DOM樹上胚泌,然后進行一定的布局處理省咨,比如標記節(jié)點塊在瀏覽器中的坐標等形成最終的渲染樹,最后根據(jù)這棵渲染樹在瀏覽器窗口中進行繪制玷室,最終我們就看到了頁面的樣子零蓉。當(dāng)然在頁面渲染過程中還會同步進行javascript的解析,而且這兩者是在同一個線程中的穷缤,所以一旦javascript死循環(huán)敌蜂,頁面的渲染也就進行進行不下去了。我們可以看到打開知乎網(wǎng)頁需要處理HTML,CSS,JavaScript,XHR,圖片津肛,剩余的2s便是在進行頁面的渲染章喉。
點擊Firebug控制臺左上角的箭頭會發(fā)現(xiàn)頁面中的每一塊內(nèi)容在HTML中都有對應(yīng)的語句,如圖7所示身坐。