網(wǎng)站優(yōu)化離不開前后端的互相協(xié)作,但是對于前端工程師來說澈灼,在保證后端技術(shù)方案不變時,能不能只利用前端技術(shù)來優(yōu)化網(wǎng)站呢店溢?答案是肯定叁熔。雅虎的郵件團隊總結(jié)了常用的35條網(wǎng)站優(yōu)化最佳實踐,其中就有很多實踐床牧,只要我們前端人員在日常開發(fā)過程中遵循這些實踐荣回,就可以在一定程度優(yōu)化網(wǎng)站加載速度。
本文篇幅較長戈咳,主要內(nèi)容就是網(wǎng)站優(yōu)化的35條最佳實踐心软,以及對每條最佳實踐的說明壕吹。技術(shù)文章看起來都是很枯燥,特別對于這種篇幅較長的文章删铃,這里先列出來有哪些實踐耳贬,不是每條實踐的說明都要看,才能明白這個實踐的意思猎唁。
1.最小化HTTP請求 | 2.使用內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN) |
3.增加Expires或者Cache-Control頭 | 4.Gzip壓縮內(nèi)容 |
5.把CSS文件放到頂部 | 6.把Js文件放到底部 |
7.避免CSS表達式 | 8.保持Js和CSS外部引用 |
9.減少DNS查詢路徑 | 10.壓縮js和css |
11.避免重定向 | 12.刪除重復(fù)Scripts |
13.配置Etags | 14.使Ajax請求可緩存 |
15.盡早刷新緩存 | 16.使用GET方式的Ajax請求 |
17.延遲加載內(nèi)容 | 18.預(yù)加載組 |
19.減少DOM元素數(shù)量 | 20.跨域分離內(nèi)容 |
21.減少iframe | 22.不要404 |
23.減少Cookie大小 | 24.內(nèi)容使用沒有cookie的域 |
25.減少DOM訪問次數(shù) | 26.優(yōu)化事件處理 |
27.選擇<link>而不是@import | 28.避免Filter |
29.優(yōu)化Images | 30.優(yōu)化CSS Sprites |
31.不要在HTML中使用過大的Images | 32.favicon.ico最小化以及可緩存 |
33.內(nèi)容保持在25K以下 | 34.把內(nèi)容打包成一個復(fù)合文檔 |
35.避免空的Image src |
下面就是這些實踐的詳細說明咒劲。
1.最小化HTTP請求
80%的終端響應(yīng)時間花費在前端,這其中的大部分時間又浪費在下載頁面內(nèi)容上诫隅,頁面內(nèi)容包括圖片腐魂,樣式表,腳本逐纬,flash等等蛔屹。減少頁面內(nèi)容的數(shù)量,轉(zhuǎn)而就減少了渲染頁面需要的HTTP請求數(shù)量豁生。這是優(yōu)化頁面的關(guān)鍵判导。
減少頁面內(nèi)容的一種方法是簡化頁面設(shè)計。但是是否存在其它方法既可以用豐富的內(nèi)容來構(gòu)建頁面沛硅,又可以獲取快速的相應(yīng)時間呢?下面的幾種技術(shù)即可以減少HTTP請求數(shù)量绕辖,又可以支持豐富的頁面設(shè)計摇肌。
合并文件是一種減少HTTP請求的方式,通過合并多個Js文件到一個Js文件仪际,合并多個CSS文件到一個CSS文件的方式围小。文件合并是非常有挑戰(zhàn)性的,因為每個頁面的Script和CSS都不一樣树碱,但是如果在你的發(fā)布過程中有這個步驟確實可以響應(yīng)時間肯适。
CSS Sprites是減少圖片請求首選的方法。把你的背景圖片合并到一張圖片中成榜,使用CSS的background-image和background-position屬性來展示期望的圖片部分框舔。
Image maps 合并多個圖片到一個圖片中。圖片總大小是一樣的赎婚,但是減少了一定數(shù)量的HTTP請求刘绣,加快了頁面展示速度。只有當(dāng)頁面中的圖片是連續(xù)的挣输,Image maps才有用纬凤,例如導(dǎo)航欄。定義Image maps的坐標可能非常乏味并且也容易出錯撩嚼,為導(dǎo)航使用Image maps也不是很方便停士,所以不推薦這種方式挖帘。
Inline images 使用data:URL scheme把圖片數(shù)據(jù)內(nèi)嵌到當(dāng)前頁面中。這種方式會增加HTML文檔的大小恋技。把Inline images寫到(緩存的)樣式文件中是減少HTTP請求的同時避免增加頁面大小的一種方法拇舀。Inline images還沒有被所有的主流瀏覽器支持。
2.使用內(nèi)容分發(fā)網(wǎng)絡(luò)(Content Delivery Network,CDN)
用戶跟Web服務(wù)器的距離對相應(yīng)時間是有影響的猖任。從用戶的角度來說你稚,把你的內(nèi)容部署在多個地理位置分散的服務(wù)器上有利于頁面加載的更快。
實施內(nèi)容分發(fā)朱躺,首先不要嘗試把Web應(yīng)用重構(gòu)成分布式架構(gòu)刁赖。根據(jù)不同的應(yīng)用,改變架構(gòu)可能會有艱巨的任務(wù)长搀,比如在不同的服務(wù)器之前同步session狀態(tài)宇弛,復(fù)制數(shù)據(jù)庫事務(wù)。這樣的話應(yīng)用架構(gòu)這一步可能就會導(dǎo)致這種嘗試延遲源请。
記住80%到90%的終端響應(yīng)時間花費在下載頁面內(nèi)容上面:iamges,css,js,flash等等枪芒,這是性能的黃金法則。而不是以重構(gòu)web應(yīng)用架構(gòu)的艱巨任務(wù)開始谁尸,最好首先分散靜態(tài)內(nèi)容舅踪。這樣不僅最大化的減少響應(yīng)時間,而且利用內(nèi)容分發(fā)網(wǎng)絡(luò)可以變得更簡單良蛮。
內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN)是分布在多個地點的web服務(wù)器的集合抽碌,可以有效的為用戶分發(fā)內(nèi)容。給用戶分發(fā)內(nèi)容的服務(wù)器是根據(jù)網(wǎng)絡(luò)距離選擇的决瞳,例如货徙,選擇擁有最少網(wǎng)絡(luò)跳轉(zhuǎn)或者最快相應(yīng)速度的服務(wù)器。
3.增加Expires或者Cache-Control頭
這個規(guī)則有兩個方面:
對于靜態(tài)資源:實行"永不過期"策略皮胡,通過在header中把Expires設(shè)置盡量長點
對于動態(tài)資源:使用一個適當(dāng)?shù)腃ache-Control header幫助瀏覽器響應(yīng)條件請求
頁面設(shè)計的越來越豐富痴颊,這就意味著頁面中有更多的js,css,images,falsh等。首次訪問頁面的用戶可能會有幾個HTTP請求屡贺,但是通過使用Expires header你可以使這些內(nèi)容緩存蠢棱。這樣在后續(xù)頁面訪問時可以避免不必要的HTTP請求次數(shù)。Expires header經(jīng)常和圖片一起用烹笔,但是js,css,flash都可以使用裳扯。
瀏覽器(和代理)使用緩存減少HTTP請求數(shù)量和大小,加快頁面加載速度谤职。web服務(wù)器在HTTP響應(yīng)中使用Expires header告訴客戶端內(nèi)容可以被緩存多長時間饰豺。 下面就是一個Expires header例子,告訴瀏覽器這個響應(yīng)在2010-4-15之前不會過期Expires: Thu, 15 Apr 2010 20:00:00 GMT
如果是Apache服務(wù)器允蜈,使用ExpiresDefault指令相對當(dāng)前日期設(shè)置過期時間冤吨。下面就是設(shè)置請求10年過期的例子:ExpiresDefault "access plus 10 years"
謹記蒿柳,如果你使用了Expires header,那么當(dāng)你的文件內(nèi)容有改變時漩蟆,內(nèi)容的文件名必須改變垒探。這樣內(nèi)容才會更新〉±睿可以再文件名中加版本后圾叼,這樣每次發(fā)布時文件名都會改變。
使用Expire header只會影響已經(jīng)訪問過你站點的用戶捺癞。如果用戶第一次訪問站點或者瀏覽器還沒有緩存夷蚊,它不會減少HTTP請求次數(shù)。因此這種性能改善的影響依賴于用戶是不是經(jīng)常訪問帶緩存的頁面髓介。通過使用Expires header你可以讓瀏覽器增加緩存內(nèi)容數(shù)量惕鼓,在后續(xù)頁面訪問時重用這些內(nèi)容而不用在通過網(wǎng)絡(luò)請求獲取。
4.Gzip壓縮內(nèi)容
前端工程師做出的某些決定可以顯著的減少HTTP請求以及響應(yīng)的網(wǎng)絡(luò)傳輸時間唐础。終端用戶的帶寬速度箱歧,網(wǎng)絡(luò)服務(wù)提供商,距網(wǎng)絡(luò)交換節(jié)點的距離這些因素都不受開發(fā)團隊的控制一膨。但是仍有其它的因素會影響響應(yīng)時間呀邢。比如,通過壓縮HTTP響應(yīng)來減少響應(yīng)時間豹绪。
從HTTP/1.1開始驼鹅,web客戶端就開始支持HTTP請求的 Accept-Encoding header。Accept-Encoding: gzip, deflate
如果web服務(wù)器在請求中看到這種header,它就會用客戶端列出來的方法來壓縮響應(yīng)內(nèi)容森篷。web服務(wù)器通過響應(yīng)中的Content-Encoding header通知web客戶端。Content-Encoding: gzip
Gzip是目前最流行以及最有效的壓縮方法豺型。其它你有可能看到的壓縮格式是deflate,但是它不是很流行以及很有效仲智。
Gzip通常可以把響應(yīng)內(nèi)容大小減少70%姻氨。目前瀏覽器當(dāng)中接近90%的網(wǎng)絡(luò)流量支持gzip钓辆。
對于瀏覽器以及代理來說還有一個已知問題就是瀏覽器期望的內(nèi)容和它獲取的壓縮內(nèi)容可能不匹配。幸運的是肴焊,這種情況隨著舊版瀏覽器的使用率越來越低會越來越少前联。Apache模塊通過添加適當(dāng)?shù)牟煌憫?yīng)頭來解決這個問題。
服務(wù)端選擇用Gzip壓縮內(nèi)容主要依賴于文件類型娶眷,但是通常也受限于要決定壓縮的內(nèi)容似嗤。大部分web站點用gzip壓縮html文檔。當(dāng)然也值的壓縮腳本以及樣式文件届宠,但是很多網(wǎng)站都沒有選擇這么做烁落。事實上乘粒,可以用gzip壓縮任何文本響應(yīng)內(nèi)容,包括XML和JSON伤塌。Image和PDF不建議gzip壓縮灯萍,因為它們都是被壓縮過的。試圖壓縮它們不僅浪費CPU也有可能會增加文件大小每聪。
用gzip壓縮盡可能多的文件類型是減少頁面大小提升用戶體驗的一種簡單方法旦棉。
5.把CSS文件放到頂部
把CSS文件移到文檔的HEAD中看起來好像可以加快頁面載入速度,其實這是因為把CSS文件放到HEAD中药薯,可以讓頁面逐步渲染绑洛。
前端工程師可能只關(guān)注性能,希望頁面可以逐步加載果善,但是對于用戶來說诊笤,不管頁面有多少內(nèi)容,希望瀏覽器可以盡快的展示頁面巾陕。對于頁面內(nèi)容很多以及用戶網(wǎng)絡(luò)環(huán)境不好的情況讨跟,這一點非常重要。給用戶視覺反饋(例如進度提示)的重要性不言而喻鄙煤,有很多類似的研究晾匠。當(dāng)瀏覽器加載頁面時逐步加載頭部,導(dǎo)航欄梯刚,以及頂部logo等的過程凉馆,HTML頁面就相當(dāng)于進度提示,這樣就給等待頁面加載的用戶一個視覺反饋亡资。提高整體的用戶體驗澜共。
如果把CSS文件放到頁面底部,在很多瀏覽器中可能會影響逐步渲染過程锥腻,包括IE瀏覽器嗦董。因為這些瀏覽器會阻塞渲染過程以防頁面元素樣式改變時重繪元素。用戶就會看到一個空白頁瘦黑。
HTML的規(guī)范中明確聲明了CSS是包含在頁面的HEAD中的京革。
****6.把Js文件放到底部****
腳本文件會導(dǎo)致的一個問題就是它們會阻塞并行下載。HTTP/1.1規(guī)范建議不要在一個主機上并行下載不超過兩個腳本文件幸斥。如果把圖片托管到多臺主機上面匹摇,那就可以并行下載多個圖片文件。但是當(dāng)一個腳本文件正在下載時甲葬,瀏覽器不會下載其他的腳本文件廊勃,即便這個腳本文件在其它主機上面。
在有些情況下想要把腳本文件移動文檔底部并不容易经窖。例如如果腳本使用document.write插入頁面內(nèi)容供搀,它就不能移到頁面底部隅居。這可能會引起作用域問題。在很多情況下葛虐,還有其它的途徑解決類似的這種問題胎源。
一個常用的建議就是使用延時腳本。script的DEFER屬性表明腳本不包含document.write屿脐,告訴瀏覽器可以繼續(xù)渲染涕蚤。但是也不是所有的瀏覽器都支持。Firefox不支持DEFER屬性的诵,IE對DEFER的支持也不盡如意万栅。
如果腳本可以延遲加載,它就可以放到頁面底部西疤,這樣就會加快頁面的載入速度烦粒。
7.避免CSS表達式
CSS表達式是動態(tài)設(shè)置CSS屬性的一種強大并且危險的方法。IE從5開始支持CSS表達式代赁,但是從IE8開始棄用這種方式扰她。下面的例子就是,背景顏色可以根據(jù)時間動態(tài)設(shè)置:
background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );
如上面展示的芭碍,expression方法接受一個js表達式參數(shù)徒役,CSS的屬性是根據(jù)js表達式的計算結(jié)果設(shè)置的。其它瀏覽器不支持expression方法窖壕,所以它只適用于IE瀏覽器忧勿。
CSS表達式的問題是計算太頻繁了,不僅頁面渲染縮放時瞻讽,需要計算鸳吸,頁面滾動甚至鼠標滑過頁面時都需要重新計算。如果在CSS表達式里面加個計數(shù)器來追蹤CSS表達式的計算頻率速勇,把鼠標滑過頁面就會發(fā)現(xiàn)計算次數(shù)會輕易的超過10000次层释。
減少CSS表達式計算次數(shù)的方法是使用一次性表達式,表達式第一次計算時會給樣式屬性設(shè)置一個顯示值來代替表達式快集。如果樣式屬性必須根據(jù)頁面活動動態(tài)設(shè)置,用事件綁定代替CSS表達式是一個好的選擇廉白。
8.保持Js和CSS外部引用
有很多性能規(guī)則來處理外部組件管理个初。然而,在考慮這些因素之前猴蹂,有一個更基本的問題需要考慮:js和css應(yīng)該包含在外部文件中院溺,還是內(nèi)聯(lián)在頁面里面?
使用外部文件通常會加快頁面速度磅轻,因為這樣js,css文件可以被瀏覽器緩存珍逸。如果js,css內(nèi)聯(lián)在頁面中逐虚,每次請求頁面的時候都會重新加載。這樣雖然減少了http請求數(shù)量谆膳,但是增加了html的大小叭爱。從另一方面來說,如果js,css在外部文件中并且被瀏覽器緩存漱病,那么后續(xù)請求不會增加html文檔大小买雾,也不會增加http請求數(shù)量(外部文件從瀏覽器緩存中取,不經(jīng)過http請求了)杨帽。
關(guān)鍵是html文檔使用緩存的js,css文件的頻率漓穿。這個因素盡管難以量化,但是也可以通過其它維度衡量注盈。如果一個站點用戶每個session周期需要訪問多個頁面晃危,并且這多個頁面有共同的腳本和樣式文件,那么使用緩存的文件就有潛在的好處老客。
一般情況下都是把js,css寫到外部文件中僚饭,但是也有例外,內(nèi)聯(lián)適合應(yīng)用在首頁沿量。首頁的每個session的訪問次數(shù)少浪慌,內(nèi)聯(lián)js,css可以加快終端的響應(yīng)時間。
內(nèi)聯(lián)可以降低http請求朴则,使用外部文件緩存也有好處权纤。通常的處理是首頁內(nèi)聯(lián)js,css,當(dāng)也加載完成之后再動態(tài)下載外部文件乌妒,這樣后續(xù)頁面就可以從瀏覽器緩存中獲取這些文件汹想。
9.減少DNS查詢路徑
域名系統(tǒng)(DNS)是把域名映射到IP地址上,就像手機通訊錄把人名映射到手機號上一樣撤蚊。當(dāng)你在瀏覽器輸入www.yahoo.com的時候古掏,DNS解析服務(wù)連接到瀏覽器然后返回服務(wù)器的IP地址。DNS是有開銷的侦啸,一般會花費20-120ms為主機查詢IP地址槽唾。瀏覽器不能通過主機名下載任何東西,除非DNS解析已完成光涂。
為了性能考慮DNS查詢都會緩存庞萍。這個緩存可能發(fā)生在某一臺緩存服務(wù)器上面,這個緩存服務(wù)器有用戶的ISP或者局域網(wǎng)維護忘闻,也有可能緩存到個別用戶電腦上钝计。DNS信息保存在操作系統(tǒng)的DNS緩存中(windows上面的DNS client service)。大部分瀏覽器都是自己的DNS緩存以區(qū)分于操作系統(tǒng)的緩存。一旦瀏覽器在自己的緩存中保存了DNS記錄私恬,它就不會從操作系統(tǒng)里面查這條記錄了债沮。
IE默認緩存DNS查詢30分鐘,可以在注冊表DnsCacheTimeout中指定本鸣。Firefox默認緩存DNS查詢1分鐘疫衩,可以通過network.dnsCacheExpiration配置項設(shè)置。
當(dāng)客戶端的DNS緩存都是空時(瀏覽器和操作系統(tǒng)的),DNS的查詢次數(shù)就和頁面中主機的DNS查詢次數(shù)一樣永高。包括頁面url隧土,圖片,腳本樣式文件,flash對象里面的主機名命爬。減少獨立主機名曹傀,就可以減少DNS查詢次數(shù)。
減少獨立主機名對頁面中需要并行下載的地方有潛在的影響饲宛。避免DNS查詢雖然可以減少響應(yīng)時間皆愉,但是減少并行下載也會增加響應(yīng)時間。這兩者是相互矛盾的艇抠,作者的建議是保持2到4個單獨主機幕庐,這樣DNS查詢不至于過多,也有一定的并行下載家淤。
和中的代碼塊也應(yīng)該壓縮异剥。盡管你用gzip壓縮了你的腳本和樣式文件,再次壓縮它們?nèi)匀豢梢詼p少5%或者更多的體積絮重。隨著使用的js和css越來越多冤寿,壓縮代碼可以消減成本。
10.壓縮js和css
壓縮的做法就是移除代碼中不必要的字符減少文件大小進而提升加載速度青伤。代碼一旦被壓縮督怜,所有的注釋,不需要的空白字符(空格狠角,換行号杠,tab)都會被移除。對于Js來說丰歌,這會提高響應(yīng)時間性能姨蟋,因為要下載的文件大小減少了。常用的壓縮js代碼的工具是JSMin立帖,YUI Compressor眼溶。YUICompressor也可以壓縮css。和中的代碼塊也應(yīng)該壓縮厘惦。盡管你用gzip壓縮了你的腳本和樣式文件,再次壓縮它們?nèi)匀豢梢詼p少5%或者更多的體積。隨著使用的js和css越來越多宵蕉,壓縮代碼可以消減成本酝静。
代碼混淆是處理源代碼的另一種選擇,它比代碼壓縮更復(fù)雜羡玛,因此在混淆過程中也更容易產(chǎn)生問題别智。有調(diào)查指出代碼壓縮可以減少21%的大小,代碼混淆可以減少25%的大小稼稿。盡快代碼混淆有更高的壓縮率薄榛,但是代碼壓縮風(fēng)險更低。
除了外部的腳本樣式需要壓縮让歼,內(nèi)聯(lián)在<script>
和<style>
中的代碼塊也應(yīng)該壓縮敞恋。盡管你用gzip壓縮了你的腳本和樣式文件,再次壓縮它們?nèi)匀豢梢詼p少5%或者更多的體積谋右。隨著使用的js和css越來越多硬猫,壓縮代碼可以消減成本。
11.避免重定向
重定向是使用301,302狀態(tài)碼來完成的改执。下面就是一個使用301響應(yīng)的HTTP header:
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html
瀏覽器自動的把用戶帶到Location字段制定的url中啸蜜。重定向所有有用額信息都在header里面。響應(yīng)內(nèi)容通常是空的辈挂。
12.刪除重復(fù)Scripts
一個頁面中有兩個一樣的js文件會影響性能衬横。這可能不像你想的那樣不同尋常。當(dāng)團隊人數(shù)及腳本數(shù)量增加時终蒂,很有可能一個頁面就會引入相同的腳本文件蜂林。這種情況發(fā)生時,重復(fù)的腳本會創(chuàng)建不需要HTTP連接而且多次執(zhí)行進而會影響性能后豫。不需要的HTTP請求發(fā)生在IE瀏覽器中悉尾,F(xiàn)irefox不會有這種現(xiàn)象。在IE中挫酿,如果一個外部js被引入了兩次并且沒被緩存构眯,它就會在頁面加載時產(chǎn)生兩個HTTP請求。甚至當(dāng)js被緩存時早龟,當(dāng)用戶刷新頁面后惫霸,額外的HTTP請求也會發(fā)生。除了產(chǎn)生浪費的HTTP請求葱弟,腳本多次也會浪費時間壹店。這種情況在Firefox以及IE中都會發(fā)生,不管js文件有沒有被緩存芝加。
有一種方法可以避免多次引入相同的腳本硅卢,那就是在模板系統(tǒng)里面實現(xiàn)一個腳本管理模塊。這樣的話,在HTML頁面中就可以利用script標簽引入腳本管理文件将塑。<script type="text/javascript" src="menu_1.0.17.js"></script>
這種方式不僅可以避免相同的腳本插入多次脉顿,還可以處理腳本相關(guān)的其它情況,比如点寥,依賴檢查艾疟,把版本號加到腳本文件名后面就可以實現(xiàn)緩存腳本更新。
13.配置Etags
ETags(Entity tags)是Web服務(wù)器和瀏覽器使用的一種機制敢辩,來決定瀏覽器中緩存的組件是否匹配源服務(wù)器上的組件蔽莱。(entity和component一樣,代指:圖片戚长,腳本盗冷,樣式等)ETags提供了一種可以驗證entities是否修改的機制。一個ETag是一個字符串可以唯一標示一個指定版本的組件历葛。唯一的格式要求就是這個字符串必須加引號正塌。源服務(wù)器用ETag響應(yīng)頭指定組件的ETag。
HTTP/1.1 200 OK
Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
ETag: "10c24bc-4ab-457e1c1f"
Content-Length: 12195
然后恤溶,如果瀏覽器需要驗證組件乓诽,它使用If-None-Match頭把ETag傳到源服務(wù)器。如果ETags匹配咒程,會返回一個304狀態(tài)碼鸠天,這樣可以減少12195字節(jié)的響應(yīng)。
GET /i/yahoo.gif HTTP/1.1
Host: us.yimg.com
If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
If-None-Match: "10c24bc-4ab-457e1c1f"HTTP/1.1 304 Not Modified
ETags的問題在于它們通常用屬性來構(gòu)建帐姻,這些屬性使它們對于特定的網(wǎng)站服務(wù)器是獨有的稠集。當(dāng)瀏覽器從服務(wù)器獲取源組件,然后嘗試在不同的服務(wù)器上驗證這個組件時饥瓷,ETags不會匹配的剥纷。這種情況在使用集群服務(wù)器處理請求時很常見。默認情況下呢铆,Apache和IIS在ETag中嵌入數(shù)據(jù)晦鞋,這樣大大降低了在有多個服務(wù)器的站點上進行有效性測試成功的幾率。
Apache 1.3和2.x版本的ETag格式是inode-size-timestamp棺克。盡管一個指定的文件在不同的服務(wù)器中保存在相同的目錄悠垛,有著相同的大小,權(quán)限娜谊,時間戳等等确买,但是不同服務(wù)器的inode是不一樣的。
IIS 5.0和6.0版本也有ETags問題纱皆。IIS的Etags格式是Filetimestamp:ChangeNumber
湾趾。ChangeNumber是一個追蹤IIS配置更改的計數(shù)器芭商。一個站點的所有的IIS服務(wù)器的ChangeNumber不可能一樣。Apache和IIS的ETags問題導(dǎo)致的結(jié)果就是搀缠,對于相同的組件在各個服務(wù)器之間不會匹配蓉坎。如果ETags沒有匹配,用戶就不會收到小的胡嘿,快速的304響應(yīng);反而會收到一個正常的200響應(yīng)和組件內(nèi)容一起钳踊。如果web站點只部署在一臺機器上衷敌,就不會有這個問題。但是如果有多臺服務(wù)器拓瞪,而且又使用了Apache或者IIS的默認ETag配置缴罗,用戶獲取頁面的速度就不會那么快,服務(wù)器有更高的負載祭埂,消耗更多的帶寬面氓,代理也不會緩存有效的內(nèi)容。即使組件有一個很大的過期時間蛆橡,但是每當(dāng)用戶點擊重載或刷新時舌界,都會創(chuàng)建一個條件GET請求。
如果你沒有利用ETags提供的靈活驗證模型泰演,最好移除ETag呻拌。Last-Modified頭驗證基于組件的時間戳。移除ETag可以減小響應(yīng)和后續(xù)請求的HTTP頭的大小睦焕。在Apache中可以通過修改配置文件來實現(xiàn):FileETag none藐握。
14.使Ajax請求可緩存
使用Ajax的一個好處就是它能給用戶提供及時的響應(yīng),因為它從后端異步的獲取數(shù)據(jù)垃喊。然而使用Ajax并不能保證用戶可以立馬得到返回的異步j(luò)s和xml響應(yīng)猾普。在許多應(yīng)用中,用戶是否繼續(xù)等待本谜,取決于Ajax如果如何使用初家。例如在內(nèi)嵌頁面的郵件客戶端中用戶為了獲取符合搜索條件的郵件消息,會繼續(xù)等待Ajax的請求結(jié)果耕突。重要的是要記住一點笤成,“異步”并不代表“及時”。
為了提高性能眷茁,優(yōu)化Ajax響應(yīng)也很重要炕泳。提高Ajax性能最重要的方式是緩存響應(yīng)內(nèi)容。來看一個例子上祈,一個Web2.0的郵箱客戶端可能使用Ajax來自動下載用戶地址列表培遵。如果用戶從上次使用郵箱web app之后沒有修改過地址列表浙芙,那么之前地址列表響應(yīng)可以從緩存中讀取,如果Ajax響應(yīng)用Expires或者Cache-Control頭做了緩存。瀏覽器必須知道何時使用緩存地址列表對于一個新請求赏殃”陂牛可以通過添加一個時間戳到地址列表Ajax url后面,表明用戶修改地址列表的最新時間南窗,例如,&t=1190241612郎楼。如果地址列表自從上次下載都沒有被修改万伤,時間戳應(yīng)該不變,然后就會從瀏覽器緩存中去地址列表呜袁,這樣就消除了一個多余的HTTP請求敌买。如果用戶修改了地址列表,時間戳就確保了新的url不會匹配到緩存中的響應(yīng)阶界,瀏覽器會請求更新的地址列表虹钮。
盡管Ajax響應(yīng)是動態(tài)創(chuàng)建的,也可能只適用于單個用戶膘融,也應(yīng)該被緩存芙粱。這樣做可以使你的Web2.0 apps更快。
15.盡早刷新緩存
當(dāng)用戶請求一個頁面時氧映,后端可能會消耗200或者500ms來整合HTML頁面宅倒。在這段時間內(nèi),瀏覽器處在等待服務(wù)器返回數(shù)據(jù)的空閑狀態(tài)屯耸。在PHP中拐迁,可以使用flush()函數(shù)。它可以讓服務(wù)器發(fā)送部分準備好的html響應(yīng)給瀏覽器疗绣,然后當(dāng)后端正在處理html頁面的其余部分時线召,瀏覽器可以開始獲取內(nèi)容。
刷新緩存的最佳位置是在HEAD標簽之后多矮,因為HTML頁面的head通常更容易產(chǎn)生缓淹,而且它可以引入任何CSS和JS文件,這樣當(dāng)后端還在處理時塔逃,瀏覽器也可以開始并行的下載引用文件讯壶。
... <!-- css, js -->
</head>
<?php flush(); ?>
<body>
... <!-- content -->
16.使用GET方式的Ajax請求
雅虎郵件團隊發(fā)現(xiàn)當(dāng)使用XMLHttpRequest時,瀏覽器器實施POST請求有兩步湾盗,第一步發(fā)送headers,然后發(fā)送數(shù)據(jù)伏蚊。所以最好使用GET請求,它只發(fā)一個TCP包(除非cookie數(shù)量很多)格粪。在IE中url的最大長度是2K躏吊,如果數(shù)據(jù)超過2K氛改,不能使用GET請求。
如果POST請求沒有發(fā)送數(shù)據(jù)比伏,那么它的行為就像POST一樣胜卤。基于HTTP協(xié)議赁项,GET請求是用來獲取信息的葛躏,所以當(dāng)你僅僅是獲取數(shù)據(jù)而不是向服務(wù)端發(fā)送數(shù)據(jù)時,最好使用GET請求悠菜。
17.延遲加載組件
看一眼頁面紫新,然后思考下:“那些是一定需要在初始化渲染頁面時?”李剖。其余的內(nèi)容和組件都可以延遲獲取。
js文件是一個理想的備選項囤耳。例如如果js代碼或者庫是用來實現(xiàn)拖拽篙顺,動畫效果的,那么就可以等待加載充择,因為頁面中的拖拽元素在初始化渲染之后的德玫。考慮可以延遲加載組件時也包括隱藏內(nèi)容(在用戶某個行為之后的才出現(xiàn)的內(nèi)容)和圖片的修飾椎麦。
當(dāng)性能目標存在于其他web開發(fā)最佳實踐中是非常好的宰僧。在這種情況下,漸進增強告訴我們js可以提升用戶體驗观挎,但是必須確保頁面沒有js時也可以正常工作琴儿。因此當(dāng)你確定頁面正常后,你可以用一些延時加載js來增強頁面效果嘁捷。
18.預(yù)加載組件
預(yù)加載看起來和延遲加載相反造成,但是它有不同的目的。通過預(yù)加載組件雄嚣,你可以充分利用瀏覽器空閑時間獲取需要的組件(圖片晒屎,樣式文件,腳本文件)缓升。這樣當(dāng)用戶訪問下個頁面時鼓鲁,你可能已經(jīng)把大部分組件緩存到瀏覽器緩存,然后頁面加載時就會更快港谊。有下面幾種類型的預(yù)加載:
無條件預(yù)加載:一旦開始加載骇吭,你就開始獲取一些額外的組件。拿google.com作為例子歧寺,看下一張sprite圖片時如何請求加載的绵跷。這張sprite圖片google首頁并不需要膘螟,但是在搜索結(jié)果頁面需要。
有條件預(yù)加載:基于用戶行為碾局,你做了一個猜測用戶下一步要去那個頁面荆残,然后預(yù)加載響應(yīng)的東西。在search.yahoo.com站點你會發(fā)現(xiàn)當(dāng)你開始在輸入框輸入時净当,一些額外的組件時如何被請求的内斯。
預(yù)期預(yù)加載:發(fā)新版本時提前加載。發(fā)布新版后像啼,可能會有這樣的抱怨“新本很酷俘闯,但是速度比之前慢”。
一部分原因可能是用戶訪問之前版本時有了充分的緩存忽冻,但是新版本剛開始沒有緩存真朗。你可以消除這方面的影響通過預(yù)加載一些組件,在你發(fā)新版之前僧诚。舊站點可以利用瀏覽器的空閑時間加載新版要使用的圖片和腳本遮婶。
19.減少DOM元素數(shù)量
一個復(fù)雜的頁面意味著要下載更多的字節(jié),js中更慢的DOM訪問速度湖笨。例如當(dāng)你遍歷500個或者5000個dom元素添加時間處理時是不同的旗扑。
大量的dom元素可能意味著有些頁面標記需要被改進而不一定需要刪除一些內(nèi)容。你是否有過嵌套表格達到布局目的慈省?使用很多<div>僅僅是修復(fù)布局問題臀防?也許對標記來說有更好的語義正確的方式。
dom元素的數(shù)量很好測試边败,在Firebug控制臺輸入:document.getElementsByTagName("*").length
袱衷。
但是多少dom元素算多呢?可以參考下有良好標記的類似頁面笑窜。
20.跨域分離組件
分離組件可以最大化的并行下載祟昭。考慮到DNS查考消耗怖侦,確保使用不超過2到4個域篡悟。例如你可以托管HMTL和動態(tài)內(nèi)容在www.example.org,將靜態(tài)組件放在static1.example.org和static2.example.org上面匾寝。
21.減少iframe
iframe允許把html文檔內(nèi)嵌到父文檔中搬葬。理解iframe的工作原理有助于更有效的使用iframe。
iframe優(yōu)點:有助于緩沖第三方內(nèi)容艳悔,像廣告急凰;安全沙箱;并行下載js;
iframe缺點:即使是空白標簽也有消耗抡锈;阻塞頁面加載疾忍;非語義;
22.不要404
HTTP請求是費時操作床三,所以發(fā)HTTP請求但是獲取一個無用響應(yīng)(例如404)是完全沒必要的一罩,也會降低用戶體驗而么有任何好處。
有些站點有一些有用的404內(nèi)容撇簿,這有助于提高用戶體驗聂渊,但是仍然會浪費服務(wù)端的資源(像數(shù)據(jù)庫等)。當(dāng)引入的一個外部js文件是有問題的而且找不到的時候四瘫,這種情況是很糟糕的汉嗽。首先這次下載會阻塞并行下載,然后瀏覽器可能會把404響應(yīng)內(nèi)容當(dāng)成js代碼解析找蜜,找出一些有用的東西饼暑。
23.減少Cookie大小
之所以會使用HTTP cookies是有多種理由的,例如權(quán)限洗做,個性化弓叛。cookies的信息在瀏覽器和服務(wù)端通過HTTP頭交換。盡量減少cookies大小有助于減少用戶響應(yīng)時間竭望。
24.組件使用沒有cookie的域
當(dāng)瀏覽器請求一個靜態(tài)圖片時,會隨請求發(fā)送cookies到服務(wù)器裕菠,但是這些cookies在服務(wù)端又沒什么用咬清。只會增加網(wǎng)絡(luò)流量。應(yīng)該確保靜態(tài)組件響應(yīng)沒有cookie請求奴潘【缮眨可以創(chuàng)建一子域?qū)iT托管靜態(tài)組件。
假設(shè)你的域名是www.example.org画髓,你可以把靜態(tài)組件托管在static.example.org掘剪。如果你已經(jīng)在頂級域名example.org上面設(shè)置了cookie而不是在www.example.org上面設(shè)置,那么所有的經(jīng)過static.example.org的請求都會攜帶cookie奈虾。這種情況你可以買一個新域名夺谁,把靜態(tài)組件托管到新域名上,并保持這個域名沒有cookie肉微。
把靜態(tài)文件托管在無cookie的域匾鸥,還有一個好處就是,有些代理可能不會緩存那些請求中帶cookie的靜態(tài)組件碉纳。如果你想知道你是應(yīng)該用example.org或者www.example.org作為主頁勿负,考慮下cookie的影響。如果忽略www,會把cookie寫到*.example.org中劳曹,所以為了性能考慮最好使用帶www的子域并且把cookie寫到子域奴愉。
25.減少DOM訪問次數(shù)
js訪問DOM元素也是耗時操作琅摩,為了更好的頁面響應(yīng),你最好做到下面幾點:
緩存訪問到的元素引用
更新"離線"節(jié)點添加到DOM樹中
避免用js操作布局
26.優(yōu)化事件處理
有時頁面可能會響應(yīng)延時锭硼,因為DOM樹中不同元素上綁定了太多的事件房资,然后這些事件執(zhí)行的太頻繁了。這就是為什么事件委托是一個好的方法账忘。如果在一個div里面有10個button,可以給div綁定一個事件而不是給每一個button綁定事件志膀。因為有事件冒泡,所以你可以捕獲到這個事件鳖擒,并且可以定位出來自于那個button溉浙。(關(guān)于事件委托可以參看這篇文章中的介紹)
如果你想開始對DOM樹做點什么,你并不需要等待onload事件蒋荚。通常你需要確定是你要獲取的標簽在DOM樹中已經(jīng)可用戳稽。你也沒必要等待所有的圖片都被下載。等到所有瀏覽器都支持DOMContentLoad事件期升,你可以用DOMContentLoad事件代替onload事件惊奇。
27.選擇<link>而不是@import
前面的最佳實踐有一點就是把CSS放到頂部,這樣可以漸進渲染播赁。在IE中@import的行為和在頁面底部使用<link>的效果一樣颂郎,所以最好不要使用它。
28.避免Filter
IE有個屬性AlphaImageLoader filter容为,這個屬性主要為了實現(xiàn)真彩色的PNG圖片在IE7以下中的半透明效果乓序。當(dāng)瀏覽器正在下載圖片時,使用這個filter會阻塞渲染讓瀏覽器停止響應(yīng)坎背。它也會增加內(nèi)存開銷替劈,會作用于每個元素而不是每個圖片。
最好的方式是完全避免使用AlphaImageLoader得滤,使用PNG8代替陨献,PNG8對IE友好。如果必須要使用AlphaImageLoader懂更,使用下劃線hack _filter以防對使用IE7+的用戶不利眨业。
29.優(yōu)化Images
設(shè)計師為網(wǎng)頁設(shè)計好圖片之后,在傳到服務(wù)器上面之前還有很多事情要做沮协。
你可以檢查下GIFs坛猪,看看他們使用的調(diào)色板大小是佛對應(yīng)圖片顏色數(shù)。使用imagemagick工具皂股,非常容易檢查墅茉,使用命令 -verbose image.gif。當(dāng)你看到一個圖片使用了4種顏色,一個256顏色“槽”在調(diào)色板中就斤,這說明還是有提升空間的悍募。
嘗試把GIFs轉(zhuǎn)成PNGs,看下大小是否減少了洋机。通常是有所減小的坠宴。開發(fā)者經(jīng)常在使用PNG時猶豫,擔(dān)心瀏覽器支持限制绷旗,但是現(xiàn)在這種事不會發(fā)生了∠补模現(xiàn)在唯一要擔(dān)心的是真彩色PNG的透明度通道,GIF不是真彩色衔肢,不支持透明度變化庄岖。因此GIF可以做的,PNG也可以做(動畫除外)角骤。在imagemagick工具中使用下面命令可以安全的使用PNG:convert image.gif image.png
在pngcrush工具(PNG優(yōu)化工具)中處理所有的PNG隅忿。在jpegtran工具中處理所有的JPEG。這個工具無損JPEG操作邦尊,可以用來優(yōu)化移除圖片中的注解以及無用信息(例如EXIF信息)背桐。
30.優(yōu)化CSS Sprites
在sprite中水平排列圖片比垂直排列圖片更剩空間蝉揍。
在sprite中組合同類色可以降低顏色數(shù)量链峭。理想情況下小于256色,這樣更適應(yīng)PNG8又沾。保持移動友好性弊仪,sprite中的圖片之間不要留大的空白。這樣雖然對文件大小沒多少影響捍掺,但是用戶代理可以更少的內(nèi)存把圖片解壓成像素地圖撼短。100X100的圖片有一萬個像素再膳,1000X1000就有一百萬個像素挺勿。
31.不要在HTML中使用過大的Images
不要使用超過你需要的大圖片,因為你可以在HTML中設(shè)置寬高喂柒。如果你需要<img width="100" height="100" src="mycat.jpg" alt="My Cat" />
不瓶,那么你的圖片(mycat.jpg)應(yīng)該是100X100,而不是500X500灾杰。
32.favicon.ico最小化以及可緩存
favicon.ico存在服務(wù)端根目錄蚊丐。即使你不關(guān)注它,但是瀏覽器仍然需要請求它艳吠,因此最好不要用404響應(yīng)麦备。因為在同一臺服務(wù)器上面,瀏覽器每次請求它時也會帶著cookie。這個圖片也會干擾下載序列凛篙,例如在IE瀏覽器中當(dāng)你請求額外的組件黍匾,favicon會在這些組件之前下載。所以為了減少favicon的缺點呛梆,需要做到下面幾點:
使用小的favicon锐涯,最好1K一下。設(shè)置一個你認為合適的Expires header填物。你可以設(shè)置Expires header為幾個月纹腌。為了做明智的決定,你可以檢查favicon.icon的上次修改時間滞磺。
33.內(nèi)容保持在25K以下
這個限制是基于這樣一個事實升薯,iphone不會緩存超過25K的內(nèi)容。注意這是未壓縮大小雁刷。這也說明了壓縮的重要性覆劈,但是僅僅gzip壓縮是不夠的。
34.把內(nèi)容打包成一個復(fù)合文檔
把內(nèi)容打包成一個復(fù)合文檔相當(dāng)于帶附件的郵件沛励,可以讓你通過一個HTTP請求獲取多個內(nèi)容责语。使用這項技術(shù)之前,先確認下用戶代理是否支持這種技術(shù)目派。
35.避免空的Image src
空src屬性的Image經(jīng)常會出現(xiàn)坤候。主要有兩種形式:
HTML形式:<img src="">
js形式:var img = new Image(); img.src = "";
這兩種形式都有同樣的影響:瀏覽器給服務(wù)端發(fā)送另外的請求。
IE對當(dāng)前頁所在目錄發(fā)請求企蹭。Safari和Chrome對當(dāng)前頁面本身發(fā)請求白筹。Firefox3和之前的版本行為和Safari和Chrome一樣,但是3.5以上版本不會發(fā)請求谅摄。Opera碰到這種情況也不會發(fā)請求徒河。
全文完