減少HTTP請求
圖片地圖
// 示例代碼
<img usemap="#map1" src="...">
<map name="map1">
<area shape="rect" coords="0,0,31,31">
...
</map>
CSS Sprites
合并圖片,使用CSS背景定位。
內聯圖片
通過使用data:URL模式可能在web頁面上包含圖片但無需任何額外的HTTP請求。
data:URL除了可以用于內聯圖片,還可以用在任何需要指定URL的地方,包括script和a標簽。
缺點是不受IE支持,另外可能存在數據大小上的限制。
由于data:URL是內聯在頁面上的,在跨越不同頁面時不會被緩存。在這種情況下,聰明的作法是使用CSS并將內聯圖片作為背景。將該CSS規則放在外部樣式表中,這意味著數據可以緩存在樣式表內部。
合并腳本和樣式表
在開發環境下分模塊,在生產環境下合并腳本和樣式表。
使用內容發布網絡(CDN)
只有10% ~ 20%的最終用戶響應時間花在下載HTML文檔上。其余的80% ~ 90%時間花在了下載頁面中的所有組件上。
使用CDN將組件Web服務器分散開(組件服務器離用戶距離越近,響應時間越短),不僅能達到響應時間大幅減少的目的,而且還很容易實現。
添加Expires頭
Web服務器使用Expires頭來告訴Web客戶端它可以使用一個組件的當前副本,來減少HTTP請求。
Expires: Mon, 15 Apr 2024 20:00:00 GMT
Cache-Control: max-age
Expires頭使用一個特定的時間,它要求服務器跟客戶端嚴格同步。HTTP1.1引入了cache-control頭來克服Expires頭的限制。
Cache-Control使用max-age指定組件被緩存多久,一個長久的的max-age頭可以將刷新窗設置為未來10年。
Cache-Control: max-age=315360000
Expires-Default
Apache模塊的mod_expires提供Expires-Default指令同時向響應中發送Expires頭和Cache-Control max-age頭。(跨瀏覽器改善緩存的最佳解決方案)
當我們需要更改緩存中的文件時,最有效的解決方案是修改其鏈接,這樣,全新的請求將從服務器下載最新的內容。雅虎的做法是將版本號嵌入文件名中。
當我們沒有使用Expires頭時,瀏覽器也會有緩存,只不過瀏覽器會向服務器發送一條GET請求,確定是否要使用緩存中文件。
壓縮組件
從HTTP1.1開始,Web客戶端可以通過HTTP請求中的Accept-Encoding頭來表示對壓縮的支持:
Accept-Encoding: gzip, deflate
如果Web服務器看到請求中有這個頭,就會使用客戶端列出來的方法中的一種來壓縮響應。Web服務器通過響應中的Content-Encoding來通知Web客戶端:
Content-Encoding: gzip
代理緩存
可以通過設置Vary: Accept-Encoding讓代理服務器緩存壓縮和未壓縮的響應,以滿足不同瀏覽器的需求(瀏覽器是否支持如gzip)。
邊緣情形
發送內容到不支持它的客戶端,忘記將壓縮內容聲明為已經進行了gzip編碼等,頁面都會被破壞。
- 如果你的網站用戶很少,并且他們處在一個小圈子中(例如,他們在一個intranet中,或者都使用Firefox 1.5)邊緣情形瀏覽器就不需要太多關注。可以壓縮內容并使用Vary:Accept-Encoding。這樣可以通過減少組件的大小和利用代理緩存來改善用戶體驗。
- 如果你更注意帶寬開銷,可以和前一種情況一樣——壓縮內容并使用Vary: Accept-Encoding。這降低了服務器端的帶寬開銷并提升了代理處理的請求數量。
- 如果你擁有大量的,多變的用戶群,能夠應付較高的帶寬開銷,并且享有高質量的名聲,請壓縮內容并使用Cache-Control: Private。這禁用了代理緩存但避免了邊緣情形缺陷。
將樣式表放在頂部
在IE中,將樣式表放在底部會導致出現“白屏”。
一個style塊可以包含多個@import規則,但@import規則必須放在所有其他規則之前。
<style>
@import url("styles2.css");
</style>
@import規則也有可能會導致白屏現象,因為使用@import規則會導致組件下載時的無序性。即便把@import規則放在文檔的HEAD標簽中也是如此。
無樣式內容的閃爍
把樣式表放在最后的情況:頁面加載時,文字首先顯示,然后是圖片,最后在樣式表正確地下載并解析之后,已經呈現的文字和圖片要用新的樣式重繪了,這就是“無樣式內容的閃爍”。而白屏現象正是對這個問題的彌補。
將腳本放在底部
下載腳本時并行下載實際上是被禁用的,即使使用了不同的主機名,瀏覽器也不會啟動其他的下載。其中一個原因是,腳本可能使用document.write來修改頁面內容,因此瀏覽器會等待,以確保頁面能夠恰當地布局。
在下載腳本時瀏覽器阻塞并行下載的另一個原因是為了保證腳本能夠按照正確的順序執行。如果并行下載多個腳本,就無法保證響應是按照特定順序到達瀏覽器的。
避免CSS表達式
使用外部JavaScript和CSS
內聯的JavaScript和CSS可以減少額外的HTTP請求,但是使用外部的JavaScript和CSS有機會被瀏覽器緩存起來,在這種情況下,HTML文檔會比使用內聯的HTML文檔小,而且不會增加HTTP請求的數量。
使用外部JavaScript和CSS能提高組件重用率,并且易維護。
對于很多網站的主頁來說,使用內聯JavaScript和CSS會是更好的選擇。然后在主頁加載完成后下載其余頁面的外部JavaScript和CSS,使其被瀏覽器緩存起來。
動態內聯是指服務器根據cookie生成有內聯樣式的頁面或有外部鏈接的頁面。
減少DNS查找
DNS是將域名映射到IP地址,而這個查找過程需要時間。
通常大型網站會使用不同的主機,以提供更多的并行下載,這樣頁面相對能夠更快地展示給用戶。但是越多的主機,意味著需要更多的DNS查找。
精簡JavaScript
避免重定向
重定向引起的延遲也很嚴重,因為它延遲了整個HTML文檔的傳輸。在HTML文檔到達之前,頁面中不會呈現任何東西,也沒有任何組件會被下載。
URL的結尾沒有斜線會導致一次重定向,這個是可以人為避免的。但是主機后缺少斜線時不會發生重定向,如http://www.yahoo.com,你會發現瀏覽器地址欄會在結尾自動添加斜線。這是因為瀏覽器在進行GET請求時必需指定路徑。如果沒有路徑,它就會簡單的使用文檔根(/)
刪除重復腳本
在一個頁面中兩次包含同一個JavaScript文件會損傷性能,因為會產生不必要的HTTP請求(IE)和執行JavaScript所浪費的時間(腳本會被多次求值)。
在IE中,被緩存的文件還會發送條件GET請求,以確定緩存中的文件是否需要更新。
配置ETag
ETag(Entity Tag,實體標簽)是Web服務器和瀏覽器用于確認組件有效性的一種機制。
瀏覽器下載組件時,會將它們存儲到緩存中。在后續的頁面瀏覽中,如果緩存的組件是“新鮮的”,瀏覽器就會從磁盤上讀取它,避免產生HTTP請求。如果組件沒有過期,那它就是“新鮮的”,這取決于Expires頭。
如果緩存過期了,瀏覽器必須檢查它是否還有效,這時,瀏覽器會發送一個條件GET請求到服務器上進行確認。如果緩存有效,則會返回304 Not Modified的狀態碼。
檢查組件是否有效有兩種方式:
- 比較最新修改日期
- 比較實體標簽
瀏覽器緩存在組件和最新的修改日期,下次請求時,會使用If-Modified-Since頭將最新修改日期到服務器上比較
實體標簽就是比較緩存中的組件與原始服務器上的組件是否匹配。ETag提供了比最新修改日期更為靈活的機制,因為它可以使用組件的一些屬性來構造。使用If-None-Match頭將ETag發送到服務器上比較。
ETag的問題是其對于服務器的唯一性,在服務器集群中,很難做到有效的驗證。所以相同的組件在不同的服務器,收到的可能就不是304響應,而是200響應,因此會下載組件的所有數據。
ETag還降低了代理緩存的效率。代理后面用戶緩存的ETag經常和代理緩存的ETag不匹配,這導致不必要的請求被發送到原始服務器。
而且If-None-Match比If-Modified-Since優先級高。如果請求中同時出現這兩個頭,則原始服務器會禁止返回304,除非請求中的條件頭字段全部一致。實際上如果根本沒有If-None-Match頭反而會更好一些。
即便組件具有長久的Expires頭,一旦用戶單擊了Reload或Refresh按鈕,仍然會產生條件GET請求。
使Ajax可緩存
使Ajax可緩存,除了使用Expires頭之外,最好的方式是使用查詢子符串參數,如:
/ws/mail/v1/formrpc?m-GetMessage&yid=steve_souders