本來這是個老生常談的問題,上周自成又分享了一些性能優(yōu)化的建議,我這里再做一個全面的Tips整理,謹(jǐn)作為查閱型的文檔,不妥之處,還請指正。
一、 Yahoo的軍規(guī)條例:
謹(jǐn)記:80%-90%的終端響應(yīng)時間是花費(fèi)在下載頁面中的圖片,樣式表,腳本,flash等;
詳細(xì)的解釋來這里查:http://developer.yahoo.com/performance/rules.html
也可以直接firebug上一項(xiàng)項(xiàng)比對,如下圖:
簡單翻譯解釋下:
1、盡量減少HTTP請求個數(shù)——須權(quán)衡
合并圖片(如css sprites,內(nèi)置圖片使用數(shù)據(jù))、合并CSS、JS,這一點(diǎn)很重要,但是要考慮合并后的文件體積。
2、使用CDN(內(nèi)容分發(fā)網(wǎng)絡(luò))
這里可以關(guān)注CDN的三類實(shí)現(xiàn):鏡像、高速緩存、專線,以及智能路由器和負(fù)載均衡;
3、為文件頭指定Expires或Cache-Control,使內(nèi)容具有緩存性。
區(qū)分靜態(tài)內(nèi)容和動態(tài)內(nèi)容,避免以后頁面訪問中不必要的HTTP請求。
4、避免空的src和href
留意具有這兩個屬性的標(biāo)簽如link,script,img,iframe等;
5、使用gzip壓縮內(nèi)容
Gzip壓縮所有可能的文件類型以來減少文件體積
6、把CSS放到頂部
實(shí)現(xiàn)頁面有秩序地加載,這對于擁有較多內(nèi)容的頁面和網(wǎng)速較慢的用戶來說更為重要,同時,HTML規(guī)范清楚指出樣式表要放包含在頁面的<head />區(qū)域內(nèi);
7、把JS放到底部
HTTP/1.1 規(guī)范建議,瀏覽器每個主機(jī)名的并行下載內(nèi)容不超過兩個,而問題在于腳本阻止了頁面的平行下載,即便是主機(jī)名不相同
腳本帶來的問題就是它阻止了頁面的平行下載。HTTP/1.1 規(guī)范建議,瀏覽器每個主機(jī)名的并行下載內(nèi)容不超過兩個。如果你的圖片放在多個主機(jī)名上,你可以在每個并行下載中同時下載2個以上的文件。但是當(dāng)下載腳本時,瀏覽器就不會同時下載其它文件了,即便是主機(jī)名不相同。
8、避免使用CSS表達(dá)式
頁面顯示和縮放,滾動、乃至移動鼠標(biāo)時,CSS表達(dá)式的計(jì)算頻率是我們要關(guān)注的?梢钥紤]一次性的表達(dá)式或者使用事件句柄來代替CSS表達(dá)式。
9、將CSS和JS放到外部文件中
我們需要權(quán)衡內(nèi)置代碼帶來的HTTP請求減少與通過使用外部文件進(jìn)行緩存帶來的好處的折中點(diǎn)。
10、減少DNS查找次數(shù)
我們需要權(quán)衡減少 DNS查找次數(shù)和保持較高程度并行下載兩者之間的關(guān)系。
DNS解析的過程也是需要時間的。一般情況下返回給定域名對應(yīng)的IP地址會花費(fèi)20到120毫秒的時間。而且在這個過程中瀏覽器什么都不會做直到DNS查找完畢。緩存DNS查找可以改善頁面性能。減少主機(jī)名的數(shù)量可以減少DNS查找次數(shù)。減少主機(jī)名的數(shù)量還可以減少頁面中并行下載的數(shù)量。減少DNS查找次數(shù)可以節(jié)省響應(yīng)時間,
但是減少并行下載卻會增加響應(yīng)時間。
11、精簡CSS和JS
目的就是減少下載的文件體積,可考慮壓縮工具JSMin和YUI Compressor。
12、避免跳轉(zhuǎn)
為了確保“后退”按鈕可以正確地使用,使用標(biāo)準(zhǔn)的 3XXHTTP狀態(tài)代碼;同域中注意避免反斜杠 “/” 的跳轉(zhuǎn);跨域使用 Alias或者 mod_rewirte建立 CNAME(保存一個域名和另外一個域名之間關(guān)系的DNS記錄)
例如,當(dāng)我們要訪問http: //astrology.yahoo.com/astrology 時,實(shí)際上返回的是一個包含301代碼的跳轉(zhuǎn),它指向的是http://astrology.yahoo.com/astrology/(注意末尾的斜杠)。
13、剔除重復(fù)的JS和CSS
重復(fù)調(diào)用腳本,除了增加額外的HTTP請求外,多次運(yùn)算也會浪費(fèi)時間。在IE和Firefox中不管腳本是否可緩存,它們都存在重復(fù)運(yùn)算JavaScript的問題。
14、配置ETags
Entity tags(ETags)(實(shí)體標(biāo)簽)是web服務(wù)器和瀏覽器用于判斷瀏覽器緩存中的內(nèi)容和服務(wù)器中的原始內(nèi)容是否匹配的一種機(jī)制(“實(shí)體”就是所說的“內(nèi) 容”,包括圖片、腳本、樣式表等),是比last-modified date更更加靈活的機(jī)制,單位時間內(nèi)文件被修過多次,Etag可以綜合Inode(文件的索引節(jié)點(diǎn)(inode)數(shù)),MTime(修改時間)和 Size來精準(zhǔn)的進(jìn)行判斷,避開UNIX記錄MTime只能精確到秒的問題。 服務(wù)器集群使用,可取后兩個參數(shù)。使用ETags減少Web應(yīng)用帶寬和負(fù)載。
15、使AJAX可緩存
利用時間戳,更精巧的實(shí)現(xiàn)響應(yīng)可緩存與服務(wù)器數(shù)據(jù)同步更新。
16、盡早刷新輸出緩沖
尤其對于css,js文件的并行下載更有意義
17、使用GET來完成AJAX請求
當(dāng)使用XMLHttpRequest時,瀏覽器中的POST方法是一個“兩步走”的過程:首先發(fā)送文件頭,然后才發(fā)送數(shù)據(jù)。在url小于2K時使用GET獲取數(shù)據(jù)時更加有意義。
18、延遲加載
確定頁面運(yùn)行正常后,再加載腳本來實(shí)現(xiàn)如拖放和動畫,或者是隱藏部分的內(nèi)容以及折疊內(nèi)容等。
19、預(yù)加載
關(guān)注下無條件加載,有條件加載和有預(yù)期的加載。
20、減少DOM元素個數(shù)
使用更適合或者在語意是更貼切的標(biāo)簽,要考慮大量DOM元素中循環(huán)的性能開銷。
21、根據(jù)域名劃分頁面內(nèi)容
很顯然, 是最大限度地實(shí)現(xiàn)平行下載
22、盡量減少iframe的個數(shù)
考慮即使內(nèi)容為空,加載也需要時間,會阻止頁面加載,沒有語意,注意iframe相對于其他DOM元素高出1-2個數(shù)量級的開銷,它會在典型方式下阻塞onload事件,IE和Firefox中主頁面樣式表會阻塞它的下載。
23、避免404
HTTP請求時間消耗是很大的,有些站點(diǎn)把404錯誤響應(yīng)頁面改為“你是不是要找***”,這雖然改進(jìn)了用戶體驗(yàn)但是同樣也會浪費(fèi)服務(wù)器資源(如數(shù)據(jù)庫等)。最糟糕的情況是指向外部 JavaScript的鏈接出現(xiàn)問題并返回404代碼。首先,這種加載會破壞并行加載;其次瀏覽器會把試圖在返回的404響應(yīng)內(nèi)容中找到可能有用的部分當(dāng)作JavaScript代碼來執(zhí)行。
404錯誤:服務(wù)器找不到指定的資源,請求的網(wǎng)頁不存在
410錯誤:請求的網(wǎng)頁不存在(注意:410表示永久性,而404表示臨時性);
24、減少Cookie的大小
去除不必要的coockie
使coockie體積盡量小以減少對用戶響應(yīng)的影響
注意在適應(yīng)級別的域名上設(shè)置coockie以便使子域名不受影響
設(shè)置合理的過期時間。較早地Expire時間和不要過早去清除coockie,都會改善用戶的響應(yīng)時間。
25、使用無cookie的域
確定對于靜態(tài)內(nèi)容的請求是無coockie的請求。創(chuàng)建一個子域名并用他來存放所有靜態(tài)內(nèi)容。
26、減少DOM訪問
緩存已經(jīng)訪問過的有關(guān)元素
線下更新完節(jié)點(diǎn)之后再將它們添加到文檔樹中
避免使用JavaScript來修改頁面布局
27、開發(fā)智能事件處理程序
有時候我們會感覺到頁面反應(yīng)遲鈍,這是因?yàn)镈OM樹元素中附加了過多的事件句柄并且些事件句柄被頻繁地觸發(fā)。這就是為什么說使用event delegation(事件代理)是一種好方法了。如果你在一個div中有10個按鈕,你只需要在div上附加一次事件句柄就可以了,而不用去為每一個按 鈕增加一個句柄。事件冒泡時你可以捕捉到事件并判斷出是哪個事件發(fā)出的。你同樣也不用為了操作DOM樹而等待onload事件的發(fā)生。你需要做的就是等待樹結(jié)構(gòu)中你要訪問的元素出現(xiàn)。你也不用等待所有圖像都加載完畢。你可能會希望用DOMContentLoaded事件來代替 事件應(yīng)用程序中的onAvailable方法。
28、用<link>代替@import
在IE中,頁面底部@import和使用<link>作用是一樣的,因此最好不要使用它。
29、避免使用濾鏡
完全避免使用AlphaImageLoader的最好方法就是使用PNG8格式來代替,這種格式能在IE中很好地工作。如果你確實(shí)需要使用 AlphaImageLoader,請使用下劃線_filter又使之對IE7以上版本的用戶無效。
30、優(yōu)化圖像
嘗試把GIF格式轉(zhuǎn)換成PNG格式,看看是否節(jié)省空間。在所有的PNG圖片上運(yùn)行pngcrush(或者其它PNG優(yōu)化工具)
31、優(yōu)化CSS Spirite
在Spirite中水平排列你的圖片,垂直排列會稍稍增加文件大;Spirite中把顏色較近的組合在一起可以降低顏色數(shù),理想狀況是低于256 色以便適用PNG8格式;便于移動,不要在Spirite的圖像中間留有較大空隙。這雖然不大會增加文件大小但對于用戶代理來說它需要更少的內(nèi)存來把圖片解壓為像素地圖。 100×100的圖片為1萬像素,而1000×1000就是100萬像素。
32、不要在HTML中縮放圖像——須權(quán)衡
不要為了在HTML中設(shè)置長寬而使用比實(shí)際需要大的圖片。如果你需要:<img width=”100″ height=”100″ src=”mycat.jpg” alt=”My Cat” />
那么你的圖片(mycat.jpg)就應(yīng)該是100×100像素而不是把一個500×500像素的圖片縮小使用。這里在下文有更有趣的分析。
33、favicon.ico要小而且可緩存
favicon.ico是位于服務(wù)器根目錄下的一個圖片文件。它是必定存在的,因?yàn)榧词鼓悴魂P(guān)心它是否有用,瀏覽器也會對它發(fā)出請求,因此最好不要返回一 個404 Not Found的響應(yīng)。由于是在同一臺服務(wù)器上,它每被請求一次coockie就會被發(fā)送一次。這個圖片文件還會影響下載順序,例如在IE中當(dāng)你在 onload中請求額外的文件時,favicon會在這些額外內(nèi)容被加載前下載。
因此,為了減少favicon.ico帶來的弊端,要做到:文件盡量地小,最好小于1K。在適當(dāng)?shù)臅r候(也就是你不要打算再換 favicon.ico的時候,因?yàn)楦鼡Q新文件時不能對它進(jìn)行重命名)為它設(shè)置Expires文件頭。你可以很安全地 把Expires文件頭設(shè)置為未來的幾個月。你可以通過核對當(dāng)前favicon.ico的上次編輯時間來作出判斷。Imagemagick可以幫你創(chuàng)建小巧的favicon。
34、保持單個內(nèi)容小于25K
因?yàn)閕Phone不能緩存大于25K的文件。注意這里指的是解壓縮后的大小。由于單純gizp壓縮可能達(dá)不要求,因此精簡文件就顯得十分重 要。
35、打包組件成復(fù)合文本
頁面內(nèi)容打包成復(fù)合文本就如同帶有多附件的Email,它能夠使你在一個HTTP請求中取得多個組件(切記:HTTP請求是很奢侈的)。當(dāng)你使用這條規(guī) 則時,首先要確定用戶代理是否支持(iPhone就不支持)。
二、Yahoo軍規(guī)之外的場景?
1、 使用json作為數(shù)據(jù)的交換格式
Json在瀏覽器解析的效率至少高于XML一個數(shù)量級,高級瀏覽器中內(nèi)置的有生成和解析json的方法,IE6中要用額外的方法(http://json.org),不要用eval,容易引發(fā)性能和安全問題。
2、 盡可能對images和table設(shè)定寬高值
針對Yslow的不要在HTML中縮放圖像——第33條,有人會誤解為不要對圖片加寬高值,其實(shí)這條建議本身的意思是不要為了獲取一個特定大小的圖片,而去強(qiáng)行通過設(shè)置寬高值拉伸或者壓縮一個既有的圖片。建議是另存一張符合尺寸的圖片替代。
對圖片和table是設(shè)定寬高,是考慮到如果瀏覽器能立刻知道圖片或者tables的寬高,它就能夠直接呈現(xiàn)頁面而不需要通過計(jì)算元素大小后重繪,而且即便是圖片損毀而沒有展現(xiàn),也不會進(jìn)而破壞了頁面本來的布局。
有一些應(yīng)用場景需要注意:
a、批量圖片,圖片源可控同時頁面圖片寬高值不可變,比如數(shù)據(jù)庫有100張100*100的圖片要在頁面中全部展示,那么建議是都寫上
<img width=”100″ height=”120″ src=”" alt=”" />
b、批量圖片,圖片源不可控同時頁面圖片寬高值不可變,比如數(shù)據(jù)庫有100張圖片,而已知圖片有的尺寸是97*100,有的100*105,而又不可能去一張張修改另存。這里視情況而定,根據(jù)圖片尺寸與要求尺寸的偏離度,在保證圖片不拉伸變形同時不影響頁面布局的情況下,可以對圖片單獨(dú)設(shè)定寬度 100,同時對其包裹的容器設(shè)定100*100的寬高來隱藏多出來的部分,注意不能同時設(shè)置寬高以防止變形。c、批量圖片,圖片源不可控,頁面圖片寬高值不定,比如數(shù)據(jù)庫有100張各種尺寸偏差較大的,此時可不對圖片設(shè)置寬高;
其他情況不一一羅列,原則是在最大程度保證圖片不變形與圖片最大面積展現(xiàn)的前提下,盡可能為圖片設(shè)置寬高值,總之就是權(quán)衡。
Tables的寬高值同圖片,盡可能設(shè)置。
3、 拆離內(nèi)容塊
盡量用div取代tables,或者將tables打破成嵌套層次深的結(jié)構(gòu);
避免用這樣的嵌套
<table>
<table>
<table>
...
</table>
</table>
</table>
采用下面的或者div重構(gòu):
<table></table>
<table></table>
<table></table>
4、 高效的CSS書寫規(guī)則
眾所周知,CSS選擇符是從右向左進(jìn)行匹配的。
通常一個圖片列表的的小模塊
<div id="box">
<div class="hd">
<h3>我的旅途</h3>
</div>
<div class="bd">
<h4>旅途1</h4>
<ul id="pics">
<li>
<a href="#pic" title=""><img src="" alt="" /> </a>
<p>這是在<strong>圖片1</strong></p>
</li>
</ul>
</div>
</div>
為了代碼上縮進(jìn)后內(nèi)層的整潔性,我們html有可能這樣寫之外,更喜歡看這樣的css寫法:
.box{border:1px solid #ccc }
.box .hd{border-bottom:1px solid #ccc }
.box .hd h3{color:#515151}
.box .bd{color:#404040 }
.box .bd ul{margin-left:10px}
.box .bd ul li{border-bottom:1px dashed #f1f1f1}
.box .bd ul li a{text-decoration:none}
.box .bd ul li a:hover{text-decoration:underline}
.box .bd ul li a img{border:1px solid #ccc}
.box .bd ul li p{text-align:left;}
.box .bd ul li p strong{color:#ff6600}
其實(shí)寫到這里,問題已經(jīng)顯而易見了。深達(dá)五層抑或六層的嵌套,同時右邊的選擇符都是采用標(biāo)簽,在滿足我們視覺平整與代碼結(jié)構(gòu)系統(tǒng)化的時候,付出的是性能的代價。
不做進(jìn)一步的代碼書寫方式的探討,受個人習(xí)慣與應(yīng)用場景影響。這里對css選擇符按照開銷從小到大的順序梳理一下:
ID選擇符 #box類選擇符 .box類型選擇符 div相鄰兄弟選擇符 h4 + #pics子選擇符 #pics li后代選擇符 .box a{}通配選擇符 *屬性選擇符 [href=”#pic”]偽類和偽元素 a:hover
參考《高性能網(wǎng)站建設(shè)-進(jìn)階指南》,有如下建議:
避免使用統(tǒng)配規(guī)則;不要限定ID選擇符;不要限定類選擇符;讓規(guī)則越具體越好;避免使用后代選擇符;避免使用標(biāo)簽-子選擇符;質(zhì)疑子選擇符的所有用途;依靠繼承;
還要注意到,即便是頁面加載后,當(dāng)頁面被觸發(fā)引起回流(reflow)的時候,低效的選擇符依然會引發(fā)更高的開銷,顯然這對于用戶是不佳的體驗(yàn)。
三、Yahoo軍規(guī)再度挖掘會怎樣?
在網(wǎng)站性能優(yōu)化的路上,是不會有終點(diǎn)的,這也是前端工程師永不會妥協(xié)的地方。
想看到更牛P的優(yōu)化建議么,請移步這里來關(guān)注李牧童鞋的分享:
- 使用combo合并靜態(tài)資源
- Bigpipe技術(shù)合并動態(tài)數(shù)據(jù)
- Comet:基于http的服務(wù)端推技術(shù)
- 使用DataURI減少圖片請求
- 使用良好的JS,CSS版本管理方案
- 嘗試僅作必要的JS更新
- 利用本地存儲做緩存
- 關(guān)于最小化HTML
- 進(jìn)一步討論Gzip
- 進(jìn)一步討論域名劃分
- 打開keep-alive,重用HTTP連接
- 使用JSON進(jìn)行數(shù)據(jù)交換
- 保障頁面可交互性
- 縮短最快可交互時間
- 異步無阻腳本下載
- 優(yōu)化內(nèi)存使用,防止內(nèi)存泄露
- 高效的JavaScript
- 第三方代碼性能問題
- Inline腳本不要與CSS穿插使用
- 使用高效的CSS選擇器
- 進(jìn)一步討論及早Flush
- 關(guān)于視覺和心理學(xué)