1 、減少HTTP請(qǐng)求數(shù)量 (Minimize HTTP Requests)
tag:content
80%的用戶響應(yīng)時(shí)間被花費(fèi)在前端,而這其中的絕大多數(shù)時(shí)間是用于下載頁面中的圖片、樣式表、腳本以及Flash這些組件。減少這些組件的數(shù)量就可以減少展示頁面所需的請(qǐng)求數(shù),而這是提高網(wǎng)頁響應(yīng)速度的關(guān)鍵。
樸素的頁面設(shè)計(jì)當(dāng)然是減少組件的一種途徑,但有沒有能兼顧豐富的頁面內(nèi)容和快速的響應(yīng)速度的方法呢?下面就是一些不錯(cuò)的技巧,能在提供豐富的頁面展現(xiàn)的同時(shí),減少Http請(qǐng)求數(shù)量:
合并文件,通過把所有腳本置于一個(gè)腳本文件里或者把所有樣式表放于一個(gè)樣式表文件中,從而減少Http請(qǐng)求的數(shù)量。當(dāng)不同頁面需要應(yīng)用不同的腳本或樣式時(shí),合并這些文件會(huì)是一個(gè)很大的挑戰(zhàn),不過在發(fā)布網(wǎng)站時(shí)進(jìn)行這種合并,將是提高網(wǎng)站響應(yīng)速度的重要一步。
CSS Sprites是減少圖片請(qǐng)求的首選方案。把所有的背景圖片合并到一張圖中,使用CSS的background-image 和background-position 屬性去控制展現(xiàn)恰當(dāng)?shù)膱D片區(qū)域。
Image maps把多張圖片組合成為一張圖片。圖片的總大小是不變的,但減少Http請(qǐng)求數(shù)會(huì)提高頁面的響應(yīng)速度。Image maps只能用于圖片在網(wǎng)頁中相鄰的情況,比如導(dǎo)航條。制定image maps中的圖片坐標(biāo)是個(gè)很麻煩的過程,而且容易出錯(cuò)。同時(shí)給導(dǎo)航使用image maps也并不易讀,所以并不推薦使用。
內(nèi)聯(lián)圖片使用data: URL scheme 把圖片數(shù)據(jù)嵌入頁面,但這會(huì)增加Html文檔的大小。把內(nèi)聯(lián)圖片合并到你被緩存的的樣式表中是一個(gè)能既減少HTTP請(qǐng)求數(shù)又不會(huì)增加頁面大小的方法。但目前并不是所有的主流瀏覽器都支持內(nèi)聯(lián)圖片。
減少頁面的Http請(qǐng)求數(shù)量是第一步,而且對(duì)于提高用戶初次訪問體驗(yàn)是最重要的一步。正如在 Tenni Theurer的blog中的Browser Cache Usage – Exposed!里描述的,每天,有 40%-60%的訪客并沒有你的網(wǎng)站的緩存文件。提高這些初次訪客的訪問速度是提高用戶體驗(yàn)的關(guān)鍵。
2 、使用內(nèi)容分布式網(wǎng)絡(luò) (Use a Content Delivery Network)
tag:server
用戶連接你的網(wǎng)站服務(wù)器的速度影響響應(yīng)的快慢。把你的網(wǎng)站布置在多臺(tái)分布于不同地域的服務(wù)器上,會(huì)讓用戶覺得你的頁面加載速度更快。那么我們應(yīng)該從哪里開始呢?
不要一開始就把重新設(shè)計(jì)你的網(wǎng)站使其能夠適應(yīng)分布式結(jié)構(gòu)作為實(shí)現(xiàn)網(wǎng)站地域分布的第一步。根據(jù)你的網(wǎng)站的復(fù)雜程度不同,更新網(wǎng)站結(jié)構(gòu)的過程也許會(huì)包含諸如同步會(huì)話狀態(tài)、在服務(wù)器間復(fù)制數(shù)據(jù)庫等一系列復(fù)雜的步驟。這樣你提高用戶訪問速度的目的反而會(huì)被更新網(wǎng)站架構(gòu)的工作耽誤。
記住,用戶80-90%的訪問時(shí)間被花費(fèi)在下載頁面中的圖片、樣式表、腳本、Flash這些組件上。這是網(wǎng)站展示的黃金法則。那么與其重新設(shè)計(jì)網(wǎng)站 的結(jié)構(gòu),不如先實(shí)現(xiàn)這些靜態(tài)組件的分布。這不僅僅可以大幅減少響應(yīng)時(shí)間,而且由于內(nèi)容分布式網(wǎng)絡(luò)(content delivery networks)的存在,這將是個(gè)很簡(jiǎn)單的工作。
內(nèi)容分布式網(wǎng)絡(luò)(CDN)是一系列分布在不同地域的服務(wù)器的集合,能夠更有效的給用戶發(fā)送信息。它會(huì)根據(jù)一種衡量網(wǎng)域距離的方法,選取為某個(gè)用戶發(fā)送數(shù)據(jù)的服務(wù)器。比如,到達(dá)用戶最少跳或者最快相應(yīng)速度的服務(wù)器會(huì)被選中。
一些大型Internet公司擁有他們自己的CDN,但使用公用的CDN服務(wù)提供商更為劃算,比如 Akamai Technologies, Mirror Image Internet, 或者Limelight Networks。對(duì)于剛起步的公司和個(gè)人網(wǎng)站來說,CDN服務(wù)的花費(fèi)也許會(huì)偏高。但當(dāng)你的目標(biāo)用戶越來越多而且趨于國際化,用CDN來降低響應(yīng)時(shí)間就很 必要了。在Yahoo!,把靜態(tài)的內(nèi)容從自己的網(wǎng)絡(luò)服務(wù)器移到CDN提高了用戶20%甚至更多的訪問速度。轉(zhuǎn)向CDN會(huì)是一個(gè)只需要相對(duì)簡(jiǎn)單的代碼更新的 工作,而且那將會(huì)顯著的提高你的網(wǎng)站訪問速度。
3 、給頭部添加一個(gè)失效期或者Cache-Control (Add an Expires or a Cache-Control Header)
tag:server
這條法則包含兩方面:
* 對(duì)于靜態(tài)組件:把頭部的緩存期設(shè)為某個(gè)遙遠(yuǎn)的未來,使其能夠“永不過期”。
* 對(duì)于動(dòng)態(tài)組件:使用適當(dāng)?shù)腃ache-Control頭部幫助瀏覽器執(zhí)行特定的請(qǐng)求。
網(wǎng)頁設(shè)計(jì)越來越豐富,頁面里包含了越來越多的腳本、樣式表、圖片和Flash。頁面的初次訪問者也許會(huì)發(fā)送多個(gè)HTTP請(qǐng)求,但通過給頭部添加失效 期,你可以使那些組件被緩存。這會(huì)避免下次瀏覽頁面時(shí)的不必要的HTTP請(qǐng)求。給圖片文件的頭部設(shè)置失效時(shí)間更為常用,但包括腳本文件、樣式表和 Flash之類的所有組件的頭部都應(yīng)該被設(shè)置失效時(shí)間。
瀏覽器(還有代理服務(wù)器)使用緩存以減少HTTP請(qǐng)求的數(shù)量和大小,提高網(wǎng)頁的加載速度。服務(wù)器在HTTP相應(yīng)中通過頭部中的過期時(shí)間告知客戶端一個(gè)組件可以被緩存多久。下面是一個(gè)far future的過期頭部,告訴瀏覽器這個(gè)響應(yīng)直到2010年4月15日才會(huì)過期:
Expires: Thu, 15 Apr 2010 20:00:00 GMT
如果你使用的是Apache服務(wù)器,使用ExpiresDefault 指令會(huì)設(shè)置一個(gè)相對(duì)于當(dāng)前時(shí)間的過期時(shí)間。這里有一個(gè)通過ExpiresDefault 指令把過期時(shí)間設(shè)為請(qǐng)求時(shí)間之后10年的例子:
ExpiresDefault “access plus 10 years”
記住,如果你使用了far future過期頭部,你必須在組件更新時(shí)更換它的名字。在Yahoo!我們通常在建設(shè)網(wǎng)站的過程中執(zhí)行這樣的步驟:組件的名字里包含了它的版本,比如:yahoo_2.0.6.js。
使用一個(gè)far future過期頭部只會(huì)在用戶已經(jīng)訪問你的網(wǎng)站之后起作用。它不會(huì)影響一個(gè)沒有緩存的初次訪問者的HTTP請(qǐng)求數(shù)量。所以這一切的效果取決于多少用戶訪 問頁面時(shí)有一份預(yù)緩存(一份”預(yù)緩存”中已經(jīng)包含了頁面的所有組件)。我們對(duì)此在Yahoo!做過測(cè)試,發(fā)現(xiàn)擁有預(yù)緩存的用戶在 75-85%。給頭部添加far future失效期,可以增加瀏覽器緩存的組件數(shù)量并重復(fù)用于隨后的頁面瀏覽而不需要通過用戶的網(wǎng)絡(luò)發(fā)送哪怕一個(gè)字節(jié)。
4、 Gzip壓縮組件(Gzip Components)
tag:server
前臺(tái)工程師的決策能夠顯著的減少在網(wǎng)絡(luò)上傳輸 HTTP請(qǐng)求和響應(yīng)花費(fèi)的時(shí)間。確實(shí),終端用戶的帶寬速度、Internet服務(wù)提供商和連接交換機(jī)的服務(wù)器這些因素都是開發(fā)小組所不能控制的。但還有一 些其它因素會(huì)影響響應(yīng)的時(shí)間,比如壓縮文件,就會(huì)減少HTTP響應(yīng)的大小從而減少響應(yīng)的時(shí)間。
從HTTP/1.1開始,Web客戶端就被設(shè)定為支持HTTP請(qǐng)求的頭部中Accept-Encoding指定的壓縮格式:
Accept-Encoding: gzip, deflate
當(dāng)服務(wù)器檢測(cè)到請(qǐng)求頭部中的這一代嗎,它就會(huì)使用客戶端提供的方法列表中的一個(gè)來壓縮響應(yīng)內(nèi)容。而服務(wù)器通過響應(yīng)頭部中的Content- Encoding來告知客戶端它所使用的壓縮方式:
Content-Encoding: gzip
Gzip是當(dāng)前最常用也是最有效的壓縮方式,GNU項(xiàng)目開發(fā)了這一方法并且符合RFC 1952標(biāo)準(zhǔn)。另外一種你可能見過的壓縮格式是deflate,但它沒有那么有效和常用。
使用gzip壓縮通常會(huì)減少70%的響應(yīng)大小。當(dāng)前瀏覽器中大約90%的Internet通訊傳輸聲明支持gzip。如果你使用Apache服務(wù) 器,配置gzip的模塊取決于服務(wù)器的版本:Apache 1.3 使用mod_gzip ,而Apache 2.x 使用mod_deflate。
瀏覽器和代理會(huì)有一些已知的問題,可能導(dǎo)致瀏覽器的預(yù)期內(nèi)容和獲得的實(shí)際壓縮內(nèi)容不匹配。幸運(yùn)的是,這種情況隨著舊瀏覽器的使用者減少而減少。 Apache的模塊可以通過自動(dòng)添加適當(dāng)?shù)淖兓憫?yīng)文件頭來解決這些問題。
服務(wù)器會(huì)根據(jù)文件類型選擇gzip壓縮的內(nèi)容,但一般情況下,服務(wù)器選擇壓縮的內(nèi)容會(huì)過于局限。大部分網(wǎng)站會(huì)壓縮它們的Html文檔,而壓縮腳本和 樣式表也是值得一做的,但很多網(wǎng)站并沒有這樣做,事實(shí)上,壓縮在包括 XML和JSON在內(nèi)的任何文本響應(yīng)都是值得的。圖片和PDF文件不應(yīng)該被gzip壓縮,因?yàn)樗鼈円呀?jīng)是被壓縮了的文件,gzip它們不僅浪費(fèi)CPU甚至 還有增大文件大小的可能。
Gzip盡可能多的文件類型是減少頁面大小從而提高用戶體驗(yàn)的一個(gè)簡(jiǎn)單的方法。
5、 把樣式表放在前面(Put Stylesheets at the Top)
tag:css
在研究Yahoo!的性能時(shí),我們發(fā)現(xiàn)把樣式表挪到文檔的頭部可以讓頁面的加載顯得更快。因?yàn)榘褬邮奖矸旁陬^部可以讓頁面逐步呈現(xiàn)。
關(guān)心網(wǎng)站性能的前臺(tái)工程師通常希望頁面能夠逐步加載;即,我們希望瀏覽器能夠把已經(jīng)獲得的內(nèi)容盡快展現(xiàn)。這對(duì)于內(nèi)容很多的頁面以及網(wǎng)絡(luò)連接較慢的用 戶尤為重要。給予用戶視覺上的反饋(比如進(jìn)度提示)的重要性,已經(jīng)經(jīng)過了很詳盡的論證。而對(duì)于我們來說,Html 頁面本身就可以作為進(jìn)度提示!當(dāng)瀏覽器逐步加載頁面時(shí),頭部、導(dǎo)航條、頂部的logo等等這些都可以作為對(duì)正在等待頁面的用戶的可視的反饋。而這會(huì)從整體 上提高用戶體驗(yàn)。
把樣式表放在文檔的最后,會(huì)導(dǎo)致包括IE在內(nèi)的大部分瀏覽器不進(jìn)行逐步呈現(xiàn)。瀏覽器為了避免當(dāng)樣式改變時(shí)重繪元素而中止呈現(xiàn)。用戶會(huì)十分無聊的看到一個(gè)空白的頁面。
Html規(guī)范明確規(guī)定樣式表應(yīng)該被包含在頁面的HEAD中:“和A不同,LINK只能在文檔的HEAD部位出現(xiàn),但它可以出現(xiàn)多次。”空白的屏幕或者由于沒有應(yīng)用樣式而引起的內(nèi)容的閃現(xiàn)都不值得去嘗試。最好的方法是遵循HTML規(guī)范,把樣式表放在文檔的HEAD部位。
6、 把腳本放在最后(Put Scripts at the Bottom)
tag:javascript
腳本可能會(huì)堵塞并發(fā)的下載。HTTP/1.1規(guī)范建議瀏覽器在每個(gè)域名下只進(jìn)行兩個(gè)并發(fā)下載。如果你把圖片放在多個(gè)域名下,可以實(shí)現(xiàn)多于兩個(gè)的并發(fā)下載。當(dāng)腳本被下載時(shí),即使使用不同的域名。瀏覽器也不會(huì)進(jìn)行任何其它的下載。
有些情況下把腳本放到底部并不太容易。比如,腳本使用了document.write 來添加部分頁面中的內(nèi)容,就不能放到頁面中更后面的位置。還可能有作用域的問題。很多情況下,還有一些變通的方法。
通常的建議是使用延遲腳本。DEFER屬性表明腳本不包含document.write,而且提示瀏覽器繼續(xù)展現(xiàn)。不幸的是,F(xiàn)irefox不支持 DEFER屬性。IE中,腳本可以被延遲,但并不如你期望的那么久。如果一個(gè)腳本可以被延遲,那么它也可以被放在頁面的底部。這會(huì)讓你的頁面加載的更快。
7、 不使用CSS表達(dá)式 (Avoid CSS Expressions)
tag:css
CSS表達(dá)式是一種有力的(同時(shí)也很危險(xiǎn)的)動(dòng)態(tài)設(shè)置CSS屬性的方法。從IE5開始支持CSS表達(dá)式。比如,使用CSS表達(dá)式可以實(shí)現(xiàn)背景顏色每小時(shí)變換的效果。
background-color: expression_r( (new Date()).getHours()%2 ? “#B8D4FF” : “#F08A00″ );
如上所示,表達(dá)式方法采用了 Javascript的表達(dá)。CSS屬性則被設(shè)為Javascript表達(dá)式的結(jié)果。其它的瀏覽器會(huì)忽略CSS表達(dá)式,所以對(duì)于設(shè)置專屬IE的屬性以便在不同瀏覽器間能有一致的體驗(yàn)是有用的。、
而CSS表達(dá)式的問題是它比大多數(shù)人期望的執(zhí)行次數(shù)更頻繁。表達(dá)式不僅僅在頁面展現(xiàn)和重新設(shè)置大小的時(shí)候執(zhí)行,在頁面滾動(dòng),甚至用戶在頁面上挪動(dòng)鼠 標(biāo)時(shí)都會(huì)執(zhí)行。給CSS表達(dá)式添加一個(gè)計(jì)數(shù)器可以跟蹤C(jī)SS在什么時(shí)候和怎樣執(zhí)行。在頁面上移動(dòng)鼠標(biāo)可以輕易的產(chǎn)生一萬次以上的執(zhí)行。
使用一次性的表達(dá)式是減少CSS表達(dá)式的執(zhí)行次數(shù)的一個(gè)方法,當(dāng)表達(dá)式第一次執(zhí)行時(shí),CSS表達(dá)式會(huì)被一個(gè)確定的值代替。如果在頁面生命周期中,樣 式屬性必須動(dòng)態(tài)的設(shè)定,使用事件處理替代CSS表達(dá)式是一個(gè)可選的方法。如果必須使用CSS表達(dá)式,要記得它們會(huì)執(zhí)行上千次并影響頁面的性能。
8、 使用外部的JavaScript和CSS (Make JavaScript and CSS External)
tag:javascript,css
很多性能規(guī)則都是解決怎樣處理獨(dú)立的組件的問題的。但是,考慮這些之前,你應(yīng)該先考慮一個(gè)更基本的問題:JavaScript和CSS應(yīng)該被放于外部的文件,還是內(nèi)聯(lián)在頁面里?
在實(shí)際應(yīng)用中使用外部的文件往往產(chǎn)生更快的頁面,因?yàn)闉g覽器會(huì)緩存JavaScript和CSS文件。而內(nèi)聯(lián)在頁面里的JavaScript和 CSS會(huì)在每次請(qǐng)求頁面時(shí)下載。這會(huì)減少所需的HTTP請(qǐng)求數(shù),但增大HTML文檔的體積。而另一方面,如果放在外部文件里的JavaScript和 CSS被瀏覽器緩存,則既不用增加HTTP請(qǐng)求的數(shù)量,HTML文檔的體積也會(huì)減少。
關(guān)鍵的問題是,外部的JavaScript和CSS的組件被緩存的頻率和HTML文檔被請(qǐng)求的次數(shù)相關(guān)。雖然很難去量化,但可以被用很多指標(biāo)衡量。 如果你的網(wǎng)站的用戶在每個(gè)會(huì)話中瀏覽了很多網(wǎng)頁而且很多頁面重用了相同的JavaSctipt和樣式表,緩存外部文件是有很大潛在的好處的。
很多網(wǎng)站都符合這樣的指標(biāo)。對(duì)于這些網(wǎng)站來說,最好的解決方案是把JavaScript和CSS發(fā)布為單獨(dú)的文件。唯一的例外,對(duì)于主頁,內(nèi)聯(lián)的文 件更好一些,例如 Yahoo!’s front page 和 My Yahoo!。主頁在每個(gè)會(huì)話中只有很少瀏覽(也許只有一次),你會(huì)發(fā)現(xiàn)內(nèi)聯(lián)的 JavaScript和CSS會(huì)讓終端用戶的響應(yīng)更快。
對(duì)于有很多頁面瀏覽量的首頁來說,有很多能平衡內(nèi)聯(lián)文件所提供的HTTP請(qǐng)求減少的效果與外部文件緩存獲得的好處的技巧。一種這樣的技巧就是把JavaScript和CSS內(nèi)聯(lián)在說夜里,但在頁面完成加載時(shí)動(dòng)態(tài)下載外部文件。隨后的頁面會(huì)調(diào)用瀏覽器中已經(jīng)緩存的外部文件。
9、 減少DNS的查詢 (Reduce DNS Lookups)
tag:content
正如電話簿使人名和他們的電話號(hào)碼相對(duì)應(yīng),域名系統(tǒng)(DNS)能夠使域名和IP地址相對(duì)應(yīng)。當(dāng)你在瀏覽器中鍵入 http://www.yahoo.com,瀏覽器鏈接的DNS解析器會(huì)返回服務(wù)器的 IP地址。域名解析會(huì)耗費(fèi)一些時(shí)間,DNS查找給定域名的IP地址一般會(huì)耗費(fèi)20-120毫秒。在DNS查找結(jié)束前,瀏覽器不會(huì)從目標(biāo)域名那里下載任何東 西。
DNS查詢會(huì)被緩存以便優(yōu)化性能。會(huì)有一個(gè)專門的緩存服務(wù)器進(jìn)行緩存,用戶的ISP或者本地網(wǎng)絡(luò)會(huì)維護(hù)它,但獨(dú)立用戶的電腦里也會(huì)有緩存。DNS信 息存在于操作系統(tǒng)的DNS緩存里(微軟Windows操作系統(tǒng)里的“DNS客戶服務(wù)”)。大部分瀏覽器有它們自己的緩存,與操作系統(tǒng)的緩存相獨(dú)立。當(dāng)瀏覽 器在自己的緩存里保存了DNS的記錄,它不會(huì)向操作系統(tǒng)發(fā)出請(qǐng)求記錄的要求。
IE默認(rèn)緩存DNS查詢30分鐘,在注冊(cè)表的DnsCacheTimeout的鍵值中設(shè)定。Firefox則緩存DNS查詢一分鐘,在配置network.dnsCacheExpiration 中設(shè)定。(Fasterfox 將它變?yōu)橐恍r(shí)。)
當(dāng)客戶端的DNS緩存被清空(包括瀏覽器和操作系統(tǒng)的緩存),DNS查詢的數(shù)量等同于網(wǎng)頁中單獨(dú)的域名的數(shù)量。包括頁面中的鏈接,圖片,腳本文件,樣式表,F(xiàn)lash對(duì)象等。減少不同域名的數(shù)量則會(huì)減少DNS查詢的數(shù)量。
減少不同域名的數(shù)量可能減少頁面并行的下載數(shù)量。減少 DNS查詢縮短了響應(yīng)時(shí)間,但減少了并行下載數(shù)也許會(huì)增加響應(yīng)時(shí)間。我的建議是將組件分布在兩到四個(gè)域名之間。這能很好的折中減少DNS查詢提高的速度和維持較高水平的并行下載的效果。
10、 縮小JavaScript和CSS (Minify JavaScript and CSS)
tag:javascript,css
縮小是指從代碼中刪除不必要的字母,減少文件體積從而提高加載速度。縮減代碼時(shí)需要移除所有的注釋,以及不需要的空白(空格,新行和tab)。這樣 處理JavaScript之后,會(huì)由于下載文件的體積被減少而提高響應(yīng)的性能。兩個(gè)常用的縮減JavaScript代碼的工具是JSMin 和YUI Compressor。YUI compressor也可以壓縮CSS。
代碼混淆是另一個(gè)可用于源代碼的優(yōu)化方案。它比壓縮更為復(fù)雜,而且在混淆的過程中更容易產(chǎn)生 Bug??v觀U.S.的前十大網(wǎng)站,壓縮獲得了21%的體積減小而代碼混淆達(dá)到了25%。雖然代碼混淆的壓縮程度更高,但壓縮JavaScript的風(fēng)險(xiǎn)更小。
不僅僅要壓縮外部的腳本和樣式表,內(nèi)斂的<script>和