1. 程式人生 > >《高效能網站建設指南》學習筆記

《高效能網站建設指南》學習筆記

線上示例:
http://stevesouders.com/hpws
序言A  前端效能的重要性
效能黃金法則:
只有10%~20%的終端使用者響應時間花在了下載HTML文件上,其餘的80%~90%時間花在了下載頁面中的所有元件上。

序言B  HTTP概述
請求型別有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE。
GET請求包含一個URL,當然是頭。
HTTP響應包含狀態碼、頭、響應體。

壓縮
如果瀏覽器和伺服器都支援的話,可以使用壓縮來減小響應的大小。
--->    GET /us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js
    HTTP 1.1
    Host: us.js2.yimg.com
    User-Agent: Mozilla/5.0(...) Gecko/20061206 Firefox/1.5.0.9
    Accept-Encoding: gzip, deflate // 瀏覽器使用Accept-Encoding頭來宣告它支援的壓縮。

<---    HTTP 1.1 200 OK
    Content-Type: application/x-javascript
    Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT
    Content-Length: 255
    Content-Encoding: gzip // 伺服器用Content-Encoding頭確認響應已被壓縮。

    ^_\213^H^@^@^@^@^@^@^C1\217\315j\3030^P\204_E\316IJ...

條件GET請求
如果瀏覽器在其快取中保留了元件的一個副本,但並不確定它是否仍然有效,就會生成一個條件GET請求。
--->    GET /us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js
    HTTP 1.1
    Host: us.js2.yimg.com
    User-Agent: Mozilla/5.0(...) Gecko/20061206 Firefox/1.5.0.9
    Accept-Encoding: gzip, deflate
    If-Modified-Since: Wed, 22 Feb 2006 04:15:54 GMT // 瀏覽器在說:“我擁有這個元件的一個版本,這是它的組後修改時間,我可以使用它嗎?”。

<---    HTTP 1.1 304 Not Modified
    Content-Type: application/x-javascript
    Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT  // 如果元件自生成日期以來沒有改變過,伺服器會返回一個“304 Not Modified”狀態碼並不再發送響應體。

Expires
條件GET請求仍需要在客戶端和伺服器之間進行一次往返確認。Expires頭通過明確指出瀏覽器是否可以元件的快取副本來消除這個需要。
<---    HTTP 1.1 200 OK
    Content-Type: application/x-javascript
    Last-Modifide: Wed, 22 Feb 2006 04:15:54 GMT
    Expires: Wed, 05 Oct 2016 19:16:20 GMT // 當瀏覽器看到Expires頭時,它會把相應的過期時間和元件一起儲存到其快取中,只要元件沒有過期,瀏覽器就會使用快取版本而不會進行任何HTTP請求。

Keep-Alive
--->    GET /us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js
    HTTP 1.1
    Host: us.js2.yimg.com
    User-Agent: Mozilla/5.0(...) Gecko/20061206 Firefox/1.5.0.9
    Accept-Encoding: gzip, deflate
    Connection: keep-alive // keep-alive使瀏覽器可以在一個單獨的連線上進行多個請求

<---    HTTP 1.1 200 OK
    Content-Type: application/x-javascript
    Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT
    Connection: keep-alive  // 同上,瀏覽器或伺服器可以通過傳送一個Connection: close頭來關閉連線

規則1  減少HTTP請求

圖片地圖:
將多張圖片合併為一張圖片,從而減少HTTP請求
伺服器圖片地圖:將所有點選提交到同一個目標URL,向其傳遞使用者單擊的x、y座標,Web應用程式將該x、y座標對映為適當的操作。
客戶端圖片地圖:將使用者的點選對映到一個操作,無需向後端應用程式傳送請求
<img usemap="#map1" border=0 src="/images/imagemap.gif?t=1196816255">
<map name="map1">
    <area shape="rect" coords="0,0,31,31" href="home.html" title="Home"> // coords為area左上角座標和右下角座標
    <area shape="rect" coords="36,0,66,31" href="gifts.html" title="Gifts">
    <area shape="rect" corrds="71,0,101,31" href="cart.html" title="Cart">
    <area shape="rect" corrds="141,0,171,31" href="help.html" title="Help">
</map>

CSS Sprites(CSS 精靈)
CSS Sprites也可以合併圖片,但更為靈活,且圖片地圖中的圖片必須是連續的,而CSS Sprites則沒有這個限制。
<style>
#navbar span {
    width:31px;
    height:31px;
    display:inline;
    float:left;
    background-image:url(/images/spritebg.gif); // div的背景圖片,即將多個圖合併為一個後的圖
}
.home        {background-position:0 0; margin-right:4px; margin-left:4px;}
.gifts        {background-position:-32px 0; margin-right:4px;}
.cart        {background-position:-64px 0; margin-right:4px;}
.settings    {background-position:-96px 0; margin-right:4px;}
.help        {background-position:-128px 0; margin-right:0px;}

<div id="navbar" style="background-color: #F4F5EB; border: 2px ridge #333; width: 180px; height: 32px; padding: 4px 0 4px 0;">
    <a href="javascript:alert('Home')" title="Home"><span class="home"></span></a> // 連線被包圍在一個SPAN中
    <a href="javascript:alert('Gifts')" title="Gifts"><span class="gifts"></span></a> // 連線被包圍在一個SPAN中
    <a href="javascript:alert('Cart')" title="Cart"><span class="cart"></span></a> // 連線被包圍在一個SPAN中
    <a href="javascript:alert('Settings')" title="Settings"><span class="settings"></span></a> // 連線被包圍在一個SPAN中
    <a href="javascript:alert('Help')" title="Help"><span class="help"></span></a> // 連線被包圍在一個SPAN中
</div>
CSS Sprites能通過合併圖片減少HTTP請求,且比圖片地圖更靈活,而且還能降低下載量。因為合併後的圖片比分離的圖片的總和要小,因為它降低了圖片自身的開銷。

內聯圖片
通過使用data:URL模式可以在Web頁面中包含圖片但無需任何額外的HTTP請求。
在下面示例中,外部樣式表的URL指向一個PHP模板---http://stevesouders.com/hpws/inline-css-images-css.php,PHP函式file_get_contents可以很容易地通過從磁碟中讀取圖片並將其內容插入到頁面中來建立內聯圖片。
.home {background-image: url(data:image/gif;base64,<?php echo base64_encode(file_get_contents("../images/home.gif"))?>);}
.gift {background-image: url(data:image/gif;base64,<?php echo base64_encode(file_get_contents("../images/gift.gif"))?>);}
.cart {background-image: url(data:image/gif;base64,<?php echo base64_encode(file_get_contents("../images/cart.gif"))?>);}
.settings {background-image: url(data:image/gif;base64,<?php echo base64_encode(file_get_contents("../images/settings.gif"))?>);}
.help {background-image: url(data:image/gif;base64,<?php echo base64_encode(file_get_contents("../images/help.gif"))?>);}

合併指令碼和樣式表
將多個指令碼和樣式表合併到一個指令碼和樣式表中,可以減少HTTP請求的數量並縮短使用者響應時間。理想情況,一個頁面應該使用一個指令碼和樣式表。

###減少HTTP請求###

規則2  使用內容釋出網路
內容釋出網路(CDN)是一組分佈在多個不同地理位置的Web伺服器,用於更加有效地向用戶釋出內容。CDN用於釋出靜態內容。無論如何不要使用HTTP重定向來將使用者指向本地伺服器,這會使Web頁面反應速度變慢。

###使用內容釋出網路CDN###

規則3  新增Expires頭
Expires頭
Expires: Thu, 15 Apr 2010 20:00:00 GMT // 在這一日期/時間之後,響應將被認為是無效的

Max-Age
HTTP 1.1 中Cache-Control使用max-age指令指定元件被快取多久,它以秒為單位定義了一個更新窗。
<---    Cache-Control: max-age=315360000 // 將重新整理窗設定為未來10年

不僅僅是圖片
長久的Expires頭應該包含任何不經常變化的元件,包括指令碼、樣式表、圖片

修訂檔名
為了確保使用者能獲得元件的最新版本,需要在所有HTML頁面中修改元件的檔名。Mark Nottingham稱:“最有效的解決方案是修改其所有連結,這樣,全新的請求將從原始伺服器下載最新的內容。
使用PHP等動態語言生成HTML頁,只需修改變數,就能將版本號嵌在元件的檔名中,而且在全域性對映中修訂過的檔名會自動更新,除錯時也很容易找到準確的原始碼。

大部分圖片、樣式表、指令碼都可快取30天以上。

###為元件新增長久的Expires頭###

規則4  壓縮元件
壓縮是如何工作的
--->    Accept-Encoding: gzip, deflate // HTTP 1.1 中,Web客戶端可以通過HTTP請求中的Accept-Encoding頭來標識對壓縮的支援。
<---    Content-Encoding: gzip // Web伺服器通過響應中的Content-Encoding頭來通知Web客戶端。

壓縮什麼
可以對HTML文件、指令碼、樣式表,以及XML和JSON在內的任何文字響應。圖片和PDF不因該壓縮,因為它們本來就已經被壓縮了。
根據經驗通常對大於1KB或2KB的檔案進行壓縮,mod_gzip_minimum_file_size指令控制著希望壓縮檔案的最小值,預設值是500B。

節省
壓縮通常能將響應的資料量減少將近70%。

代理快取
代理快取問題:傳送給代理請求的瀏覽器可能支援也可能不支援壓縮,就有可能把壓縮檔案傳送給不支援壓縮的瀏覽器
解決這一問題的方法是在Web伺服器的響應頭中新增Vary頭,Vary頭中包含Accept-Encoding
<---    Vary: Accept-Encoding

邊緣情形
新增瀏覽器白名單

###壓縮指令碼和樣式表###

規則5  將樣式表放在頂部
逐步呈現
將樣式表放在文件頂部的HEAD中,頁面將是逐步呈現的。

將CSS放在頂部
將樣式表包含在文件中有兩種方式:使用LIN標籤和@import規則。
<link rel="stylesheet" href="styles1.css">
<style>
    @import url("styles2.css");
</style> // @import規則必須放在其他規則之前,且使用@import規則會導致元件下載的無序性,可能會產生白屏。

###使用LINK標籤將樣式表放在文件HEAD中###

規則5  將樣式表放在頂部

<link rel="stylesheet" href="styles1.css"> // @import規則會導致元件下載時的無序性,可能會導致白屏現象,link語法簡單且效能更好

###使用LINK標籤將樣式表放在文件HEAD中###

規則6  將指令碼放在底部
指令碼會阻塞並行下載,會阻止頁面內容的呈現,導致白屏現象,因而放置指令碼最好的地方是頁面的底部。

###將指令碼移到頁面的底部###

規則7  避免CSS表示式
表示式不只是在頁面呈現和大小改變時求值,當頁面滾動、甚至使用者滑鼠在頁面上移過時都要求值。在頁面上來回移動滑鼠可以很輕鬆地產生10000次以上的求值。

###避免CSS表示式###

規則8  使用外部JavaScript和CSS
純粹而言,內聯快一些,但外部JavaScript和CSS檔案可以被瀏覽器快取起來。

載入後下載:既可以獲得內聯優勢,又能快取外部檔案
<script>
    function doOnload() {
        setTimeout("downloadComponents()", 1000); // 延遲1妙保證頁面呈現完畢
    }
    
    window.onload = doOnload; // onload事件

    function downloadComponents() {
        downloadJS("http://stevesouders.com/examples/testsma.js");
        downloadCSS("http://stevesouders.com/examples/testsma.css");
    }

    function downloadJS(url) {
        var elem = document.createElement("script");
        elem.src = url;
        document.body.appendChild(elem);
    }

    function downloadCSS(url) {
        var elem = document.createElement("link");
        elem.rel = "stylesheet";
        elem.type = "text/css";
        elem.href = url;
        document.body.appendChild(elem);
    }
</script>

動態內聯:可以在內聯或使用外部檔案之間作出最佳選擇。如果cookie不存在,就內聯JavaScript和CSS,且載入後下載外部檔案,如果cookie出現,則可能外部元件位於瀏覽器的快取中,就使用外部檔案。

###將JavaScript和CSS放到外部檔案中###

規則9  減少DNS查詢
減少唯一主機名可以減少DNS查詢,但會減少頁面並行下載的數量。建議使用2-4個主機名。

###通過使用Keep-Alive和較少的域名來減少DNS查詢###

規則10  精簡JavaScript
精簡:移除註釋、空格、換行、製表符。
精簡JavaScript程式碼的工具有JSMin(http://crockford.com/javascript/jsmin)和ShrinkSafe(http://dojotoolkit.org/docs/shrinksafe)
JSMin可以使JavaScript檔案的大小減小21%,ShrinkSafe可以達到25%。

###對JavaScript原始碼進行精簡###

規則11  避免重定向
重定向會延遲整個HTML文件的傳輸,在HTML文件到達之前,頁面不會呈現任何東西。

缺少結尾的斜線
在請求http://astrology.yahoo.com/astrology時會導致一個301響應,其中包含了一個到http://astrology.yahoo.com/astrology/的重定向,唯一的區別就是在結尾添加了斜線。這種重定向最為浪費、發生的也很頻繁。

跟蹤內部流量
可以使用Referer日誌來跟蹤流量去向,使用Referer日誌可以避免重定向。

跟蹤出站流量
可以使用信標來跟蹤出站流量,信標響應通常是一個1px * 1px的透明圖片,不過204響應更為優秀。

###尋找一種避免重定向的方法###

規則12  移除重複指令碼
可以在模板系統中實現一個指令碼管理模組來避免重複指令碼。

###確保指令碼只被包含一次###

規則13  配置ETag
實體標籤(Entity Tag, ETag)
--->    GET /i/yahoo.gif HTTP 1.1
    Host: us.yimg.com

<---    HTTP 1.1 200 OK
    Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
    ETag: "10c24bc-4ab-457e1c1f"
    Content-Length: 1195
ETag的問題在於,ETag對於伺服器來說是唯一的,當瀏覽器從一臺伺服器上獲取了原始元件,之後又向另外一臺伺服器傳送GET請求時,ETag不會匹配。對於擁有多臺伺服器的網站,ETag會大大降低有效性驗證的成功率。

###配置或移除ETag###

規則14  使Ajax可快取
XMLHttpRequest是Ajax的核心。

###確保Ajax請求遵守效能指導,尤其應具有長久的Expires頭。