原文地址:http://topic.csdn.net/u/20100105/11/382a3b43-e0a7-4adf-aa91-bf6ff7e25539.html
前段時間接到XX公司的電話面試,問到了類似於這樣幾個題目,
從輸入一個新的網址到使用者完全看到頁面,這期間瀏覽器做了哪些工作?
輸入網址後瀏覽器是先讀取快取檔案夾還是先串連伺服器?
.....
其實我個人覺得他問這個題重點是考瀏覽器緩衝機制,資源下載序,css渲染與js執行順序.
這個問題其實聽起來是個非常基礎的題目,但是感覺自已還是很模糊,所以本人在網上找了些資源,再加上自已的一些測試,將結果分享給大家,說錯的地方希望大家指正批評.
HTTP協議基於請求響應模式,用戶端向伺服器發送一個請求,要求標頭包含請求的方法,URI,協議版本以及包含請求修飾符,
用戶端資訊和內容的類似 MIME的訊息結果。伺服器則以一個狀態行為作為響應,相應的內容包括訊息協議的版本,
成功或錯誤編碼加上包含伺服器資訊,實體元資訊以及可能的實體內容。
緩衝:
當瀏覽器第一次請求某個URL時,順利訪問的話,伺服器返回狀態200的狀態,
同時會返回給瀏覽器一些Headers集合,例如set-cookie,Last-Mondified,Etag等等
下面重點說明Last-Mondified與Etag,即瀏覽器緩衝.
HTTP/1.x 200 OK
Server: Microsoft-IIS/7.5
Last-Modified: Thu, 31 Dec 2009 09:29:09 GMT
Etag: "e46de5b4fb89ca1:0"
Expires: Thu, 07 Jan 2010 00:00:00 GMT
Last-Mondified: 最後一次修改時間
Etag: 資源的狀態唯一標識(每個資源的etag都不同,例如img,js,css。。。。)
Expires: 指定資源在瀏覽器緩衝中的到期時間 (需要在服務端設定)
瀏覽器接收到伺服器這些資訊後,就會將資源緩衝在本地目錄中,同時儲存檔案的上述資訊.
如果有set-cookie的話,且瀏覽器未禁用cookie,則會儲存cookie資訊,當cookie到期時間大於目前時間時,瀏覽器會將cookie儲存在本地硬碟.
下次發送時會同header頭一起發送給伺服器,當然條件是相同域,path約束相符等等情況下.
第二次請求時,根據 HTTP 協議的規定,瀏覽器會向伺服器傳送 If-Modified-Since 與 If-None-Match 前序,
這兩個前序實際上是第一次請求時伺服器返回的Last-Modified,Etag.發送這兩個前序目地是詢問伺服器,該資源在時間內有沒有被修改過.
如果該資源未被修改,則伺服器會直接返回HTTP 304 (Not Changed.)狀態代碼,內容為空白,此時不會下載資源,瀏覽器則自動從緩衝目錄中讀取資源.
使用Last-Modified/Etag 可以減少傳輸成本,但不會減少http請求
測試結果(來自互連網):
如果給檔案加上關於到期時間(Expires)的header報文,這樣瀏覽器就會先檢查緩衝中的檔案,如果沒有到期,就直接使用緩衝中的檔案,從而不會發送http請求.
測試結果(來自互連網):
前面描述的只是一些普通的瀏覽器緩衝狀態,在實際應用中,如頁面跳轉(點擊頁面連結跳轉,window.open,在地址欄敲斷行符號,重新整理頁面)等操作,會有一些區別
普通頁面跳轉包括連結點擊跳轉,用js指令碼開啟新頁面(window.open),iframe時
第一次請求伺服器返回200,並返回資源的Last-Modified/Etag,
第二次請求時,瀏覽器發送上次接收的Last-Modified/Etag,伺服器直接返回304(HTTP/1.x 304 Not Modified)
如果設定了Expires,且未到期,瀏覽器直接從緩衝目錄中讀取,不發送請求給伺服器
F5重新整理時
與普通請求區別在於,即便資源設定了Expires且未到期,瀏覽器也會發送相應請求,然後根據伺服器返回狀態來決定是否下載資源.
Ctrl+F5重新整理時與無緩衝時效果一樣,伺服器返回200(資源全部重新下載).
其實明白上述的原理後,我們可以對web伺服器header頭進行合理的設定,從而可以大大提高效能.
以iis7為例,iis6設定差不多(iis管理--httpheaders選項卡--選擇允許內容到期).
例如網站下有一個images檔案夾,裡面放著很多較大的圖片,但這些圖片最少1天之內不會被更改.
那麼,我們可以給這個檔案夾設定到期時間.
在iis7下切換至該檔案夾的功能視圖--選擇iis類別中的http響應標題--設定常用標題--使web內容到期
測試:
環境:Live Http Headers + Firefox 3.5.6
在html文當中包含5張該檔案夾下的圖片.
第一次請求時,發送了6次http請求,6次下載(文檔+5張圖片)
第二次請求時,只發送了一次http請求,1次下載(文檔,而圖片則是直接從瀏覽器緩衝中讀取)
如果F5重新整理,發送了6次http請求,對於5張圖片,伺服器返回HTTP/1.x 304 Not Modified,所以瀏覽器不會下載圖片.總共1次下載.
下載資源順序
主文件當然是第一個下載,其次
IE6是按html中定義的文檔流順序來下載外部資源,從上至下.
FF則略有不同,FF會優先下載js或css,而圖片資源延遲到後面下載.
(經測試IE7,IE8也是先下載js或css,其它資源延後下載)
測試結果:
文檔:
<HTML>
<HEAD>
<TITLE> New Document </TITLE>
<link href="main.css" rel="Stylesheet" />
<script type="text/javascript" src="/AreaCounter.js"> </script>
</HEAD>
<BODY>
<div>
<img src="csdnindex_piclogo.gif" />
<link href="index090703.css" rel="Stylesheet" />
<img src="475x60ttt_2.jpg"/>
<script language='JavaScript' type='text/javascript' src='/csdn_ggmm.js'> </script>
</div>
</BODY>
</HTML>
IE7/IE8/FF下載順序:
渲染或解析(非DOM)
對於 js 運行,以及頁面載入相關事件的觸發,特別做了測試。在 Firefox 下,開啟測試頁面:
(以下測試資料來自互連網)
[22:13:32.947] HTML Start
[22:13:32.947] normal inline script run time
[22:13:34.904] normal external script run time
[22:13:35.775] [body] normal external script run time
[22:13:35.789] [body end] normal external script run time
[22:13:35.789] HTML End
[22:13:35.791] deferred inline script run time
[22:13:35.791] deferred external script run time
[22:13:35.793] DOMContentLoaded
[22:13:38.144] images[0] onload
[22:13:38.328] images[1] onload
[22:13:39.105] images[2] onload
[22:13:39.105] images[3] onload
[22:13:39.106] window.onload
很明顯,JS 的運行嚴格按照文檔流中的順序進行。其中 deferred 的指令碼會在最後運行(註:Firefox 3.5 開始支援 defer,而且支援得很完美)。
再來看下 IE8,結果如下:
[22:33:56.806] HTML Start
[22:33:56.826] normal inline script run time
[22:33:57.786] normal external script run time
[22:33:57.812] deferred inline script run time
[22:33:57.816] document.readyState = interactive
[22:33:57.934] [body] normal external script run time
[22:33:58.310] [body end] normal external script run time
[22:33:58.310] HTML End
[22:33:58.346] deferred external script run time
[22:33:58.346] images[0].readyState = loading
[22:33:58.346] images[0].readyState = complete
[22:33:58.346] images[0] onload
[22:33:58.361] doScroll
[22:33:58.451] images[1].readyState = loading
[22:33:58.479] images[1].readyState = complete
[22:33:58.479] images[1] onload
[22:33:58.794] images[2].readyState = loading
[22:33:58.854] images[2].readyState = complete
[22:33:58.854] images[2] onload
[22:33:58.876] images[3].readyState = loading
[22:33:58.876] images[3].readyState = complete
[22:33:58.876] images[3] onload
[22:33:58.887] document.readyState = complete
[22:33:58.888] window.onload
可以看出,IE8 下,defer 只對 external 指令碼有效,對 inline 指令碼無效。
css下載和渲染幾乎是同時進行的.即下載完成後會立即渲染到頁面.
當某一指令碼下載完成時,也會立刻解析和運行。指令碼的運行嚴格按照文檔流中的順序進行,deferred 的指令碼會在正常指令碼運行之後運行,
所以特別注意js指令碼放置順序.如果第二個外部指令碼中直接調用運行第一個外部指令碼中的全域變數時,會產生指令碼錯誤.
特別需要留意:指令碼運行時,會暫停該指令碼之下所有資源的下載(因為指令碼可能改變文檔流,甚至跳轉頁面,瀏覽器的暫停策略是合理的)。
至於DOM的渲染是根據瀏覽器的渲染引擎來決定的.
這裡可以瞭解各個瀏覽器的渲染引擎 :http://www.mac52ipod.cn/post/Trident-Gecko-WebKit-Presto.php
ps:defer是個很有用的東西,當某個指令碼被標識為defer=true時,瀏覽器下載完該指令碼後,不會立即執行該指令碼,而是對其它資源進行下載和解析.
預設情況下,defer為false,所以盡量不要在標識defer的js塊或外部js檔案中使用全域變數.
例子
<script type="text/javascript">
alert(a);
</script>
<script type="text/javascript">
var a=3
</script>
該指令碼會報a未定義的錯誤,如果將第一個指令碼塊增加 defer=true標記,即可正常運行.所以說加上 defer 有點類似於 window.onload,但比onload靈活!
<script type="text/javascript" defer> //等同於defer=true
alert(a);
</script>