從雅虎軍規(guī)淺談前端優(yōu)化

1.盡量減少HTTP請求數(shù)

80%的終端用戶響應(yīng)時(shí)間都花在了前端上,其中大部分時(shí)間都在下載頁面上的各種組件:圖片,樣式表肾胯,腳本,F(xiàn)lash等等耘纱。減少組件數(shù)必然能夠減少頁面提交的HTTP請求數(shù)敬肚。這是讓頁面更快的關(guān)鍵。

減少頁面組件數(shù)的一種方式是簡化頁面設(shè)計(jì)束析。但有沒有一種方法可以在構(gòu)建復(fù)雜的頁面同時(shí)加快響應(yīng)時(shí)間呢艳馒?嗯,確實(shí)有魚和熊掌兼得的辦法员寇。

  合并文件是通過把所有腳本放在一個(gè)文件中的方式來減少請求數(shù)的弄慰,當(dāng)然,也可以合并所有的CSS蝶锋。如果各個(gè)頁面的腳本和樣式不一樣的話陆爽,合并文件就是一項(xiàng)比較麻煩的工作了,但把這個(gè)作為站點(diǎn)發(fā)布過程的一部分確實(shí)可以提高響應(yīng)時(shí)間扳缕。

  CSS Sprites是減少圖片請求數(shù)量的首選方式慌闭。把背景圖片都整合到一張圖片中,然后用CSS的background-imagebackground-position屬性來定位要顯示的部分躯舔。

  圖像映射可以把多張圖片合并成單張圖片驴剔,總大小是一樣的,但減少了請求數(shù)并加速了頁面加載庸毫。圖片映射只有在圖像在頁面中連續(xù)的時(shí)候才有用仔拟,比如導(dǎo)航條。給image map設(shè)置坐標(biāo)的過程既無聊又容易出錯(cuò)飒赃,用image map來做導(dǎo)航也不容易利花,所以不推薦用這種方式科侈。

  行內(nèi)圖片(Base64編碼)data: URL模式來把圖片嵌入頁面。這樣會(huì)增加HTML文件的大小炒事,把行內(nèi)圖片放在(緩存的)樣式表中是個(gè)好辦法臀栈,而且成功避免了頁面變“重”。但目前主流瀏覽器并不能很好地支持行內(nèi)圖片挠乳。

減少頁面的HTTP請求數(shù)是個(gè)起點(diǎn)权薯,這是提升站點(diǎn)首次訪問速度的重要指導(dǎo)原則。

2.減少DNS查找

域名系統(tǒng)建立了主機(jī)名和IP地址間的映射睡扬,就像電話簿上人名和號(hào)碼的映射一樣盟蚣。當(dāng)你在瀏覽器輸入www.yahoo.com的時(shí)候,瀏覽器就會(huì)聯(lián)系DNS解析器返回服務(wù)器的IP地址卖怜。DNS是有成本的屎开,它需要20到120毫秒去查找給定主機(jī)名的IP地址。在DNS查找完成之前马靠,瀏覽器無法從主機(jī)名下載任何東西奄抽。

DNS查找被緩存起來更高效,由用戶的ISP(網(wǎng)絡(luò)服務(wù)提供商)或者本地網(wǎng)絡(luò)存在一個(gè)特殊的緩存服務(wù)器上甩鳄,但還可以緩存在個(gè)人用戶的計(jì)算機(jī)上逞度。DNS信息被保存在操作系統(tǒng)的DNS cache(微軟Windows上的”DNS客戶端服務(wù)”)里。大多數(shù)瀏覽器有獨(dú)立于操作系統(tǒng)的自己的cache妙啃。只要瀏覽器在自己的cache里還保留著這條記錄档泽,它就不會(huì)向操作系統(tǒng)查詢DNS。

IE默認(rèn)緩存DNS查找30分鐘彬祖,寫在DnsCacheTimeout注冊表設(shè)置中茁瘦。Firefox緩存1分鐘品抽,可以用network.dnsCacheExpiration配置項(xiàng)設(shè)置储笑。(Fasterfox把緩存時(shí)間改成了1小時(shí) P.S. Fasterfox是FF的一個(gè)提速插件)

如果客戶端的DNS cache是空的(包括瀏覽器的和操作系統(tǒng)的),DNS查找數(shù)等于頁面上不同的主機(jī)名數(shù)圆恤,包括頁面URL突倍,圖片,腳本文件盆昙,樣式表羽历,F(xiàn)lash對(duì)象等等組件中的主機(jī)名,減少不同的主機(jī)名就可以減少DNS查找淡喜。

減少不同主機(jī)名的數(shù)量同時(shí)也減少了頁面能夠并行下載的組件數(shù)量秕磷,避免DNS查找削減了響應(yīng)時(shí)間,而減少并行下載數(shù)量卻增加了響應(yīng)時(shí)間炼团。我的原則是把組件分散在2到4個(gè)主機(jī)名下澎嚣,這是同時(shí)減少DNS查找和允許高并發(fā)下載的折中方案疏尿。

3.避免重定向

重定向用301和302狀態(tài)碼,下面是一個(gè)有301狀態(tài)碼的HTTP頭:

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: &quot;Courier New&quot; !important; font-size: 12px !important;">HTTP/1.1 301 Moved Permanently
      Location: http://example.com/newuri
      Content-Type: text/html</pre>

瀏覽器會(huì)自動(dòng)跳轉(zhuǎn)到Location域指明的URL易桃。重定向需要的所有信息都在HTTP頭部褥琐,而響應(yīng)體一般是空的。其實(shí)額外的HTTP頭晤郑,比如ExpiresCache-Control也表示重定向敌呈。除此之外還有別的跳轉(zhuǎn)方式:refresh元標(biāo)簽和JavaScript,但如果你必須得做重定向造寝,最好用標(biāo)準(zhǔn)的3xxHTTP狀態(tài)碼磕洪,主要是為了讓返回按鈕能正常使用。

牢記重定向會(huì)拖慢用戶體驗(yàn)诫龙,在用戶和HTML文檔之間插入重定向會(huì)延遲頁面上的所有東西褐鸥,頁面無法渲染,組件也無法開始下載赐稽,直到HTML文檔被送達(dá)瀏覽器叫榕。

有一種常見的極其浪費(fèi)資源的重定向,而且web開發(fā)人員一般都意識(shí)不到這一點(diǎn)姊舵,就是URL尾部缺少一個(gè)斜線的時(shí)候晰绎。例如,跳轉(zhuǎn)到http://astrology.yahoo.com/astrology會(huì)返回一個(gè)重定向到http://astrology.yahoo.com/astrology/的301響應(yīng)(注意添在尾部的斜線)括丁。在Apache中可以用Alias荞下,mod_rewrite或者DirectorySlash指令來取消不必要的重定向。

重定向最常見的用途是把舊站點(diǎn)連接到新的站點(diǎn)史飞,還可以連接同一站點(diǎn)的不同部分尖昏,針對(duì)用戶的不同情況(瀏覽器類型,用戶帳號(hào)類型等等)做一些處理构资。用重定向來連接兩個(gè)網(wǎng)站是最簡單的抽诉,只需要少量的額外代碼。雖然在這些時(shí)候使用重定向減少了開發(fā)人員的開發(fā)復(fù)雜度吐绵,但降低了用戶體驗(yàn)迹淌。一種替代方案是用Aliasmod_rewrite,前提是兩個(gè)代碼路徑都在相同的服務(wù)器上己单。如果是因?yàn)橛蛎兓褂昧酥囟ㄏ虬η裕涂梢詣?chuàng)建一條CNAME(創(chuàng)建一個(gè)指向另一個(gè)域名的DNS記錄作為別名)結(jié)合Alias或者mod_rewrite指令。

4.讓Ajax可緩存

Ajax的一個(gè)好處是可以給用戶提供即時(shí)反饋纹笼,因?yàn)樗軌驈暮笈_(tái)服務(wù)器異步請求信息纹份。然而,用了Ajax就無法保證用戶在等待異步JavaScript和XML響應(yīng)返回期間不會(huì)非常無聊廷痘。在很多應(yīng)用程序中蔓涧,用戶能夠一直等待取決于如何使用Ajax削咆。例如,在基于web的電子郵件客戶端中蠢笋,用戶為了尋找符合他們搜索標(biāo)準(zhǔn)的郵件消息拨齐,將會(huì)保持對(duì)Ajax請求返回結(jié)果的關(guān)注。重要的是昨寞,要記得“異步”并不意味著“即時(shí)”瞻惋。

要提高性能,優(yōu)化這些Ajax響應(yīng)至關(guān)重要援岩。最重要的提高Ajax性能的方法就是讓響應(yīng)變得可緩存歼狼,就像在添上Expires或者Cache-Control HTTP頭中討論的一樣。下面適用于Ajax的其它規(guī)則:

我們一起看看例子享怀,一個(gè)Web 2.0的電子郵件客戶端用了Ajax來下載用戶的通訊錄羽峰,以便實(shí)現(xiàn)自動(dòng)完成功能。如果用戶從上一次使用之后再?zèng)]有修改過她的通訊錄添瓷,而且Ajax響應(yīng)是可緩存的梅屉,有尚未過期的Expires或者Cache-Control HTTP頭,那么之前的通訊錄就可以從緩存中讀出鳞贷。必須通知瀏覽器坯汤,應(yīng)該繼續(xù)使用之前緩存的通訊錄響應(yīng),還是去請求一個(gè)新的搀愧《枘簦可以通過給通訊錄的Ajax URL里添加一個(gè)表明用戶通訊錄最后修改時(shí)間的時(shí)間戳來實(shí)現(xiàn),例如&t=1190241612咱筛。如果通訊錄從上一次下載之后再?zèng)]有被修改過搓幌,時(shí)間戳不變,通訊錄就將從瀏覽器緩存中直接讀出迅箩,從而避免一次額外的HTTP往返消耗溉愁。如果用戶已經(jīng)修改了通訊錄,時(shí)間戳也可以確保新的URL不會(huì)匹配緩存的響應(yīng)沙热,瀏覽器將請求新的通訊錄條目叉钥。

即使Ajax響應(yīng)是動(dòng)態(tài)創(chuàng)建的,而且可能只適用于單用戶篙贸,它們也可以被緩存,而這樣會(huì)讓你的Web 2.0應(yīng)用更快枫疆。

5.延遲加載組件

可以湊近看看頁面并問自己:什么才是一開始渲染頁面所必須的爵川?其余內(nèi)容都可以等會(huì)兒。

JavaScript是分隔onload事件之前和之后的一個(gè)理想選擇息楔。例如寝贡,如果有JavaScript代碼和支持拖放以及動(dòng)畫的庫扒披,這些都可以先等會(huì)兒,因?yàn)橥戏旁厥窃陧撁孀畛蹁秩局蟮钠耘荨F渌梢匝舆t加載的部分包括隱藏內(nèi)容(在某個(gè)交互動(dòng)作之后才出現(xiàn)的內(nèi)容)和折疊的圖片碟案。

工具可幫你減輕工作量:YUI Image Loader可以延遲加載折疊的圖片,還有YUI Get utility是一種引入JS和CSS的簡單方法颇蜡。Yahoo!主頁就是一個(gè)例子价说,可以打開Firebug的網(wǎng)絡(luò)面板仔細(xì)看看。

最好讓性能目標(biāo)符合其它web開發(fā)最佳實(shí)踐风秤,比如“漸進(jìn)增強(qiáng)”鳖目。如果客戶端支持JavaScript,可以提高用戶體驗(yàn)缤弦,但必須確保頁面在不支持JavaScript時(shí)也能正常工作领迈。所以,在確定頁面運(yùn)行正常之后碍沐,可以用一些延遲加載腳本增強(qiáng)它狸捅,以支持一些拖放和動(dòng)畫之類的華麗效果。

6.預(yù)加載組件

預(yù)加載可能看起來和延遲加載是相反的累提,但它其實(shí)有不同的目標(biāo)薪贫。通過預(yù)加載組件可以充分利用瀏覽器空閑的時(shí)間來請求將來會(huì)用到的組件(圖片,樣式和腳本)刻恭。用戶訪問下一頁的時(shí)候瞧省,大部分組件都已經(jīng)在緩存里了,所以在用戶看來頁面會(huì)加載得更快鳍贾。

實(shí)際應(yīng)用中有以下幾種預(yù)加載的類型:

  • 無條件預(yù)加載——盡快開始加載鞍匾,獲取一些額外的組件。google.com就是一個(gè)sprite圖片預(yù)加載的好例子骑科,這個(gè)sprite圖片并不是google.com主頁需要的橡淑,而是搜索結(jié)果頁面上的內(nèi)容。
  • 條件性預(yù)加載——根據(jù)用戶操作猜測用戶將要跳轉(zhuǎn)到哪里并據(jù)此預(yù)加載咆爽。在search.yahoo.com的輸入框里鍵入內(nèi)容后梁棠,可以看到那些額外組件是怎樣請求加載的。
  • 提前預(yù)加載——在推出新設(shè)計(jì)之前預(yù)加載斗埂。經(jīng)常在重新設(shè)計(jì)之后會(huì)聽到:“這個(gè)新網(wǎng)站不錯(cuò)符糊,但比以前更慢了”,一部分原因是用戶訪問先前的頁面都是有舊緩存的呛凶,但新的卻是一種空緩存狀態(tài)下的體驗(yàn)男娄。可以通過在將要推出新設(shè)計(jì)之前預(yù)加載一些組件來減輕這種負(fù)面影響,老站可以利用瀏覽器空閑的時(shí)間來請求那些新站需要的圖片和腳本模闲。

7.減少DOM元素的數(shù)量

一個(gè)復(fù)雜的頁面意味著要下載更多的字節(jié)建瘫,而且用JavaScript訪問DOM也會(huì)更慢。舉個(gè)例子尸折,想要添加一個(gè)事件處理器的時(shí)候啰脚,循環(huán)遍歷頁面上的500個(gè)DOM元素和5000個(gè)DOM元素是有區(qū)別的。

大量的DOM元素是一種征兆——頁面上有些內(nèi)容無關(guān)的標(biāo)記需要清理实夹。正在用嵌套表格來布局嗎橄浓?還是為了修復(fù)布局問題而添了一堆的<div>s?或許應(yīng)該用更好的語義化標(biāo)記收擦。

YUI CSS utilities對(duì)布局有很大幫助:grids.css針對(duì)整體布局贮配,fonts.css和reset.css可以用來去除瀏覽器的默認(rèn)格式。這是個(gè)開始清理和思考標(biāo)記的好機(jī)會(huì)塞赂,例如只在語義上有意義的時(shí)候使用<div>泪勒,而不是因?yàn)樗軌蜾秩疽粋€(gè)新行。

DOM元素的數(shù)量很容易測試宴猾,只需要在Firebug的控制臺(tái)里輸入:

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: 
&quot;Courier New&quot; !important; font-size: 12px !important;">document.getElementsByTagName('*').length</pre>

那么多少DOM元素才算是太多呢圆存?可以參考其它類似的標(biāo)記良好的頁面,例如Yahoo!主頁是一個(gè)相當(dāng)繁忙的頁面仇哆,但只有不到700個(gè)元素(HTML標(biāo)簽)沦辙。

8.跨域分離組件

分離組件可以最大化并行下載,但要確保只用不超過2-4個(gè)域讹剔,因?yàn)榇嬖贒NS查找的代價(jià)油讯。例如,可以把HTML和動(dòng)態(tài)內(nèi)容部署在www.example.org延欠,而把靜態(tài)組件分離到static1.example.orgstatic2.example.org陌兑。

9.盡量少用iframe

用iframe可以把一個(gè)HTML文檔插入到父文檔里,重要的是明白iframe是如何工作的并高效地使用它由捎。

<iframe>的優(yōu)點(diǎn):

  • 引入緩慢的第三方內(nèi)容兔综,比如標(biāo)志和廣告
  • 安全沙箱
  • 并行下載腳本

<iframe>的缺點(diǎn):

  • 代價(jià)高昂,即使是空白的iframe
  • 阻塞頁面加載
  • 非語義

10.杜絕404

HTTP請求代價(jià)高昂狞玛,完全沒有必要用一個(gè)HTTP請求去獲取一個(gè)無用的響應(yīng)(比如404 Not Found)软驰,只會(huì)拖慢用戶體驗(yàn)而沒有任何好處。

有些站點(diǎn)用的是有幫助的404——“你的意思是xxx心肪?”锭亏,這樣做有利于用戶體驗(yàn),蒙畴,但也浪費(fèi)了服務(wù)器資源(比如數(shù)據(jù)庫等等)贰镣。最糟糕的是鏈接到的外部JavaScript有錯(cuò)誤而且結(jié)果是404呜象。首先膳凝,這種下載將阻塞并行下載碑隆。其次,瀏覽器會(huì)試圖解析404響應(yīng)體蹬音,因?yàn)樗荍avaScript代碼上煤,需要找出其中可用的部分。

回到頂部

css部分

11.避免使用CSS表達(dá)式

用CSS表達(dá)式動(dòng)態(tài)設(shè)置CSS屬性著淆,是一種強(qiáng)大又危險(xiǎn)的方式劫狠。從IE5開始支持,但從IE8起就不推薦使用了永部。例如独泞,可以用CSS表達(dá)式把背景顏色設(shè)置成按小時(shí)交替的:

|

1

|

background-color``: expression( (new Date()).getHours()%``2 ? ``"#B8D4FF" : ``"#F08A00" );

|

12.選擇<link>舍棄@import

前面提到了一個(gè)最佳實(shí)踐:為了實(shí)現(xiàn)逐步渲染,CSS應(yīng)該放在頂部苔埋。

在IE中用@import與在底部用<link>效果一樣懦砂,所以最好不要用它。

13.避免使用濾鏡

IE專有的AlphaImageLoader濾鏡可以用來修復(fù)IE7之前的版本中半透明PNG圖片的問題组橄。在圖片加載過程中荞膘,這個(gè)濾鏡會(huì)阻塞渲染,卡住瀏覽器玉工,還會(huì)增加內(nèi)存消耗而且是被應(yīng)用到每個(gè)元素的羽资,而不是每個(gè)圖片,所以會(huì)存在一大堆問題遵班。

最好的方法是干脆不要用AlphaImageLoader屠升,而優(yōu)雅地降級(jí)到用在IE中支持性很好的PNG8圖片來代替。如果非要用AlphaImageLoader狭郑,應(yīng)該用下劃線hack:_filter來避免影響IE7及更高版本的用戶腹暖。

14.把樣式表放在頂部

在Yahoo!研究性能的時(shí)候,我們發(fā)現(xiàn)把樣式表放到文檔的HEAD部分能讓頁面看起來加載地更快愿阐。這是因?yàn)榘褬邮奖矸旁趆ead里能讓頁面逐步渲染微服。

關(guān)注性能的前端工程師想讓頁面逐步渲染。也就是說缨历,我們想讓瀏覽器盡快顯示已有內(nèi)容以蕴,這在頁面上有一大堆內(nèi)容或者用戶網(wǎng)速很慢時(shí)顯得尤為重要。給用戶顯示反饋(比如進(jìn)度指標(biāo))的重要性已經(jīng)被廣泛研究過辛孵,并且被記錄下來了丛肮。在我們的例子中,HTML頁面就是進(jìn)度指標(biāo)魄缚!當(dāng)瀏覽器逐漸加載頁面頭部宝与,導(dǎo)航條焚廊,頂部logo等等內(nèi)容的時(shí)候,這些都被正在等待頁面加載的用戶當(dāng)作反饋习劫,能夠提高整體用戶體驗(yàn)咆瘟。

js部分

15.去除重復(fù)腳本

頁面含有重復(fù)的腳本文件會(huì)影響性能,這可能和你想象的不一樣诽里。在對(duì)美國前10大web站點(diǎn)的評(píng)審中袒餐,發(fā)現(xiàn)只有2個(gè)站點(diǎn)含有重復(fù)腳本。兩個(gè)主要原因增加了在單一頁面中出現(xiàn)重復(fù)腳本的幾率:團(tuán)隊(duì)大小和腳本數(shù)量谤狡。在這種情況下灸眼,重復(fù)腳本會(huì)創(chuàng)建不必要的HTTP請求,執(zhí)行無用的JavaScript代碼墓懂,而影響頁面性能焰宣。

IE會(huì)產(chǎn)生不必要的HTTP請求,而Firefox不會(huì)捕仔。在IE中匕积,如果一個(gè)不可緩存的外部腳本被頁面引入了兩次,它會(huì)在頁面加載時(shí)產(chǎn)生兩個(gè)HTTP請求逻澳。即使腳本是可緩存的闸天,在用戶重新加載頁面時(shí)也會(huì)產(chǎn)生額外的HTTP請求。

除了產(chǎn)生沒有意義的HTTP請求之外斜做,多次對(duì)腳本求值也會(huì)浪費(fèi)時(shí)間苞氮。因?yàn)闊o論腳本是否可緩存,在Firefox和IE中都會(huì)執(zhí)行冗余的JavaScript代碼瓤逼。

避免不小心把相同腳本引入兩次的一種方法就是在模版系統(tǒng)中實(shí)現(xiàn)腳本管理模塊笼吟。典型的腳本引入方法就是在HTML頁面中用SCRIPT標(biāo)簽:

|

1

|

<``script type="text/javascript" src="menu_1.0.17.js"></``script``>

|

16.盡量減少DOM訪問

用JavaScript訪問DOM元素是很慢的,所以霸旗,為了讓頁面反應(yīng)更迅速贷帮,應(yīng)該:

  • 緩存已訪問過的元素的索引
  • 先“離線”更新節(jié)點(diǎn),再把它們添到DOM樹上
  • 避免用JavaScript修復(fù)布局問題

17.用智能的事件處理器

有時(shí)候感覺頁面反映不夠靈敏诱告,是因?yàn)橛刑囝l繁執(zhí)行的事件處理器被添加到了DOM樹的不同元素上撵枢,這就是推薦使用事件委托的原因。如果一個(gè)div里面有10個(gè)按鈕精居,應(yīng)該只給div容器添加一個(gè)事件處理器锄禽,而不是給每個(gè)按鈕都添加一個(gè)。事件能夠冒泡靴姿,所以可以捕獲事件并得知哪個(gè)按鈕是事件源沃但。

18.把腳本放在底部

腳本會(huì)阻塞并行下載,HTTP/1.1官方文檔建議瀏覽器每個(gè)主機(jī)名下并行下載的組件數(shù)不要超過兩個(gè)佛吓,如果圖片來自多個(gè)主機(jī)名宵晚,并行下載的數(shù)量就可以超過兩個(gè)垂攘。如果腳本正在下載,瀏覽器就不開始任何其它下載任務(wù)淤刃,即使是在不同主機(jī)名下的晒他。

有時(shí)候,并不容易把腳本移動(dòng)到底部钝凶。舉個(gè)例子仪芒,如果腳本是用document.write插入到頁面內(nèi)容中的唁影,就沒辦法再往下移了耕陷。還可能存在作用域問題,在多數(shù)情況下据沈,這些問題都是可以解決的哟沫。

一個(gè)常見的建議是用推遲(deferred)腳本,有DEFER屬性的腳本意味著不能含有document.write锌介,并且提示瀏覽器告訴他們可以繼續(xù)渲染嗜诀。不幸的是,F(xiàn)irefox不支持DEFER屬性孔祸。在IE中隆敢,腳本可能被推遲,但不盡如人意崔慧。如果腳本可以推遲拂蝎,我們就可以把它放到頁面底部,頁面就可以更快地載入惶室。

javascript, css

19.把JavaScript和CSS放到外面

很多性能原則都是關(guān)于如何管理外部組件的温自,然而,在這些顧慮出現(xiàn)之前你應(yīng)該問一個(gè)更基礎(chǔ)的問題:應(yīng)該把JavaScript和CSS放到外部文件中還是直接寫在頁面里皇钞?

實(shí)際上悼泌,用外部文件可以讓頁面更快,因?yàn)镴avaScript和CSS文件會(huì)被緩存在瀏覽器夹界。HTML文檔中的行內(nèi)JavaScript和CSS在每次請求該HTML文檔的時(shí)候都會(huì)重新下載馆里。這樣做減少了所需的HTTP請求數(shù),但增加了HTML文檔的大小可柿。另一方面鸠踪,如果JavaScript和CSS在外部文件中,并且已經(jīng)被瀏覽器緩存起來了趾痘,那么我們就成功地把HTML文檔變小了慢哈,而且還沒有增加HTTP請求數(shù)。

20.壓縮JavaScript和CSS

壓縮具體來說就是從代碼中去除不必要的字符以減少大小永票,從而提升加載速度卵贱。代碼最小化就是去掉所有注釋和不必要的空白字符(空格滥沫,換行和tab)。在JavaScript中這樣做能夠提高響應(yīng)性能键俱,因?yàn)橐螺d的文件變小了兰绣。兩個(gè)最常用的JavaScript代碼壓縮工具是JSMin和YUI Compressor,YUI compressor還可以壓縮CSS编振。

混淆是一種可選的源碼優(yōu)化措施坐桩,要比壓縮更復(fù)雜转锈,所以混淆過程也更容易產(chǎn)生bug。在對(duì)美國前十的網(wǎng)站調(diào)查中,壓縮可以縮小21%内斯,而混淆能縮小25%络拌。雖然混淆的縮小程度更高乃坤,但比壓縮風(fēng)險(xiǎn)更大旬迹。

除了壓縮外部腳本和樣式,行內(nèi)的<script><style>塊也可以壓縮液斜。即使啟用了gzip模塊累贤,先進(jìn)行壓縮也能夠縮小5%或者更多的大小。JavaScript和CSS的用處越來越多少漆,所以壓縮代碼會(huì)有不錯(cuò)的效果臼膏。

圖片

21.優(yōu)化圖片

  • 嘗試把GIF格式轉(zhuǎn)換成PNG格式,看看是否節(jié)省空間示损。在所有的PNG圖片上運(yùn)行pngcrush(或者其它PNG優(yōu)化工具)

22.優(yōu)化CSS Sprite

  • 在Sprite圖片中橫向排列一般都比縱向排列的最終文件小
  • 組合Sprite圖片中的相似顏色可以保持低色數(shù)渗磅,最理想的是256色以下PNG8格式
  • “對(duì)移動(dòng)端友好”,不要在Sprite圖片中留下太大的空隙屎媳。雖然不會(huì)在很大程度上影響圖片文件的大小夺溢,但這樣做可以節(jié)省用戶代理把圖片解壓成像素映射時(shí)消耗的內(nèi)存。100×100的圖片是1萬個(gè)像素烛谊,而1000×1000的圖片就是100萬個(gè)像素了风响。

23.不要用HTML縮放圖片

不要因?yàn)樵贖TML中可以設(shè)置寬高而使用本不需要的大圖。如果需要

|

1

|

<``img width="100" height="100" src="mycat.jpg" alt="My Cat" />

|

那么圖片本身(mycat.jpg)應(yīng)該是100x100px的丹禀,而不是去縮小500x500px的圖片状勤。

24.用小的可緩存的favicon.ico(P.S. 收藏夾圖標(biāo))

favicon.ico是放在服務(wù)器根目錄的圖片,它會(huì)帶來一堆麻煩双泪,因?yàn)榧幢隳悴还芩炙眩瑸g覽器也會(huì)自動(dòng)請求它,所以最好不要給一個(gè)404 Not Found響應(yīng)焙矛。而且只要在同一個(gè)服務(wù)器上葫盼,每次請求它時(shí)都會(huì)發(fā)送cookie,此外這個(gè)圖片還會(huì)干擾下載順序村斟,例如在IE中贫导,當(dāng)你在onload中請求額外組件時(shí)抛猫,將會(huì)先下載favicon。

所以為了緩解favicon.ico的缺點(diǎn)孩灯,應(yīng)該確保:

  • 足夠小闺金,最好在1K以下
  • 設(shè)置合適的有效期HTTP頭(以后如果想換的話就不能重命名了),把有效期設(shè)置為幾個(gè)月后一般比較安全峰档,可以通過檢查當(dāng)前favicon.ico的最后修改日期來確保變更能讓瀏覽器知道败匹。

cookie

25.給Cookie減肥

使用cookie的原因有很多,比如授權(quán)和個(gè)性化讥巡。HTTP頭中cookie信息在web服務(wù)器和瀏覽器之間交換掀亩。重要的是保證cookie盡可能的小,以最小化對(duì)用戶響應(yīng)時(shí)間的影響尚卫。

  • 清除不必要的cookie
  • 保證cookie盡可能小归榕,以最小化對(duì)用戶響應(yīng)時(shí)間的影響
  • 注意給cookie設(shè)置合適的域級(jí)別,以免影響其它子域
  • 設(shè)置合適的有效期吱涉,更早的有效期或者none可以更快的刪除cookie,提高用戶響應(yīng)時(shí)間

26.把組件放在不含cookie的域下

當(dāng)瀏覽器發(fā)送對(duì)靜態(tài)圖像的請求時(shí)外里,cookie也會(huì)一起發(fā)送怎爵,而服務(wù)器根本不需要這些cookie。所以它們只會(huì)造成沒有意義的網(wǎng)絡(luò)通信量盅蝗,應(yīng)該確保對(duì)靜態(tài)組件的請求不含cookie鳖链。可以創(chuàng)建一個(gè)子域墩莫,把所有的靜態(tài)組件都部署在那兒芙委。

如果域名是www.example.org,可以把靜態(tài)組件部署到static.example.org狂秦。然而灌侣,如果已經(jīng)在頂級(jí)域example.org或者www.example.org設(shè)置了cookie,那么所有對(duì)static.example.org的請求都會(huì)含有這些cookie裂问。這時(shí)候可以再買一個(gè)新域名侧啼,把所有的靜態(tài)組件部署上去,并保持這個(gè)新域名不含cookie堪簿。Yahoo!用的是yimg.com痊乾,YouTube是ytimg.com,Amazon是images-amazon.com等等椭更。

把靜態(tài)組件部署在不含cookie的域下還有一個(gè)好處是有些代理可能會(huì)拒絕緩存帶cookie的組件哪审。有一點(diǎn)需要注意:如果不知道應(yīng)該用example.org還是www.example.org作為主頁,可以考慮一下cookie的影響虑瀑。省略www的話湿滓,就只能把cookie寫到*.example.org畏腕,所以因?yàn)樾阅茉蜃詈糜脀ww子域,并且把cookie寫到這個(gè)子域下茉稠。

移動(dòng)端

27.保證所有組件都小于25K

這個(gè)限制是因?yàn)閕Phone不能緩存大于25K的組件描馅,注意這里指的是未壓縮的大小。這就是為什么縮減內(nèi)容本身也很重要而线,因?yàn)閱渭兊膅zip可能不夠铭污。

28.把組件打包到一個(gè)復(fù)合文檔里

把各個(gè)組件打包成一個(gè)像有附件的電子郵件一樣的復(fù)合文檔里,可以用一個(gè)HTTP請求獲取多個(gè)組件(記住一點(diǎn):HTTP請求是代價(jià)高昂的)膀篮。用這種方式的時(shí)候嘹狞,要先檢查用戶代理是否支持(iPhone就不支持)。

服務(wù)器

29.Gzip組件

前端工程師可以想辦法明顯地縮短通過網(wǎng)絡(luò)傳輸HTTP請求和響應(yīng)的時(shí)間誓竿。毫無疑問磅网,終端用戶的帶寬速度,網(wǎng)絡(luò)服務(wù)商筷屡,對(duì)等交換點(diǎn)的距離等等涧偷,都是開發(fā)團(tuán)隊(duì)所無法控制的。但還有別的能夠影響響應(yīng)時(shí)間的因素毙死,壓縮可以通過減少HTTP響應(yīng)的大小來縮短響應(yīng)時(shí)間燎潮。

從HTTP/1.1開始,web客戶端就有了支持壓縮的Accept-Encoding HTTP請求頭扼倘。

Accept-Encoding: gzip, deflate

如果web服務(wù)器看到這個(gè)請求頭确封,它就會(huì)用客戶端列出的一種方式來壓縮響應(yīng)。web服務(wù)器通過Content-Encoding相應(yīng)頭來通知客戶端再菊。

Content-Encoding: gzip

|

盡可能多地用gzip壓縮能夠給頁面減肥爪喘,這也是提升用戶體驗(yàn)最簡單的方法。

30.避免圖片src屬性為空

Image with empty string src屬性是空字符串的圖片很常見纠拔,主要以兩種形式出現(xiàn):

  1. straight HTML

    <img src=””>

  2. JavaScript

    var img = new Image();
    img.src = “”;

這兩種形式都會(huì)引起相同的問題:瀏覽器會(huì)向服務(wù)器發(fā)送另一個(gè)請求秉剑。

31.配置ETags

實(shí)體標(biāo)簽(ETags),是服務(wù)器和瀏覽器用來決定瀏覽器緩存中組件與源服務(wù)器中的組件是否匹配的一種機(jī)制(“實(shí)體”也就是組件:圖片绿语,腳本秃症,樣式表等等)。添加ETags可以提供一種實(shí)體驗(yàn)證機(jī)制吕粹,比最后修改日期更加靈活种柑。一個(gè)ETag是一個(gè)字符串,作為一個(gè)組件某一具體版本的唯一標(biāo)識(shí)符匹耕。唯一的格式約束是字符串必須用引號(hào)括起來聚请,源服務(wù)器用相應(yīng)頭中的ETag來指定組件的ETag:

|

HTTP/1.1 200 OK

Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT

ETag: "10c24bc-4ab-457e1c1f"

Content-Length: 12195

|

然后,如果瀏覽器必須驗(yàn)證一個(gè)組件,它用If-None-Match請求頭來把ETag傳回源服務(wù)器驶赏。如果ETags匹配成功炸卑,會(huì)返回一個(gè)304狀態(tài)碼,這樣就減少了12195個(gè)字節(jié)的響應(yīng)體煤傍。

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">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</pre>

32.對(duì)Ajax用GET請求

Yahoo!郵箱團(tuán)隊(duì)發(fā)現(xiàn)使用XMLHttpRequest時(shí)盖文,瀏覽器的POST請求是通過一個(gè)兩步的過程來實(shí)現(xiàn)的:先發(fā)送HTTP頭,在發(fā)送數(shù)據(jù)蚯姆。所以最好用GET請求五续,它只需要發(fā)送一個(gè)TCP報(bào)文(除非cookie特別多)。IE的URL長度最大值是2K龄恋,所以如果要發(fā)送的數(shù)據(jù)超過2K就無法使用GET了疙驾。

POST請求的一個(gè)有趣的副作用是實(shí)際上沒有發(fā)送任何數(shù)據(jù),就像GET請求一樣郭毕。正如HTTP說明文檔中描述的它碎,GET請求是用來檢索信息的。所以它的語義只是用GET請求來請求數(shù)據(jù)显押,而不是用來發(fā)送需要存儲(chǔ)到服務(wù)器的數(shù)據(jù)扳肛。

33.盡早清空緩沖區(qū)

當(dāng)用戶請求一個(gè)頁面時(shí),服務(wù)器需要用大約200到500毫秒來組裝HTML頁面煮落,在這期間敞峭,瀏覽器閑等著數(shù)據(jù)到達(dá)。PHP中有一個(gè)flush()函數(shù)蝉仇,允許給瀏覽器發(fā)送一部分已經(jīng)準(zhǔn)備完畢的HTML響應(yīng),以便瀏覽器可以在后臺(tái)準(zhǔn)備剩余部分的同時(shí)開始獲取組件殖蚕,好處主要體現(xiàn)在很忙的后臺(tái)或者很“輕”的前端頁面上(P.S. 也就是說轿衔,響應(yīng)時(shí)耗主要在后臺(tái)方面時(shí)最能體現(xiàn)優(yōu)勢)。

較理想的清空緩沖區(qū)的位置是HEAD后面睦疫,因?yàn)镠TML的HEAD部分通常更容易生成害驹,并且允許引入任何CSS和JavaScript文件,這樣就可以讓瀏覽器在后臺(tái)還在處理的時(shí)候就開始并行獲取組件蛤育。

例如:

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: &quot;Courier New&quot; !important; font-size: 12px !important;"> ... <!-- css, js -->
    </head>
    <?php flush(); ?>
    <body>
      ... <!-- content --></pre>

34.使用CDN(內(nèi)容分發(fā)網(wǎng)絡(luò)

用戶與服務(wù)器的物理距離對(duì)響應(yīng)時(shí)間也有影響宛官。把內(nèi)容部署在多個(gè)地理位置分散的服務(wù)器上能讓用戶更快地載入頁面。但具體要怎么做呢瓦糕?

實(shí)現(xiàn)內(nèi)容在地理位置上分散的第一步是:不要嘗試去重新設(shè)計(jì)你的web應(yīng)用程序來適應(yīng)分布式結(jié)構(gòu)底洗。這取決于應(yīng)用程序,改變結(jié)構(gòu)可能包括一些讓人望而生畏的任務(wù)咕娄,比如同步會(huì)話狀態(tài)和跨服務(wù)器復(fù)制數(shù)據(jù)庫事務(wù)(翻譯可能不準(zhǔn)確)亥揖。縮短用戶和內(nèi)容之間距離的提議可能被推遲,或者根本不可能通過费变,就是因?yàn)檫@個(gè)難題摧扇。

記住終端用戶80%到90%的響應(yīng)時(shí)間都花在下載頁面組件上了:圖片,樣式挚歧,腳本扛稽,F(xiàn)lash等等,這是業(yè)績黃金法則滑负。最好先分散靜態(tài)內(nèi)容在张,而不是一開始就重新設(shè)計(jì)應(yīng)用程序結(jié)構(gòu)。這不僅能夠大大減少響應(yīng)時(shí)間橙困,還更容易表現(xiàn)出CDN的功勞瞧掺。

內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN)是一組分散在不同地理位置的web服務(wù)器,用來給用戶更高效地發(fā)送內(nèi)容凡傅。典型地辟狈,選擇用來發(fā)送內(nèi)容的服務(wù)器是基于網(wǎng)絡(luò)距離的衡量標(biāo)準(zhǔn)的。例如:選跳數(shù)(hop)最少的或者響應(yīng)時(shí)間最快的服務(wù)器夏跷。

35.添上Expires或者Cache-Control HTTP頭

這條規(guī)則有兩個(gè)方面:

  • 對(duì)于靜態(tài)組件:通過設(shè)置一個(gè)遙遠(yuǎn)的將來時(shí)間作為Expires來實(shí)現(xiàn)永不失效
  • 多余動(dòng)態(tài)組件:用合適的Cache-ControlHTTP頭來讓瀏覽器進(jìn)行條件性的請求

網(wǎng)頁設(shè)計(jì)越來越豐富哼转,這意味著頁面里有更多的腳本,圖片和Flash槽华。站點(diǎn)的新訪客可能還是不得不提交幾個(gè)HTTP請求壹蔓,但通過使用有效期能讓組件變得可緩存,這避免了在接下來的瀏覽過程中不必要的HTTP請求猫态。有效期HTTP頭通常被用在圖片上佣蓉,但它們應(yīng)該用在所有組件上,包括腳本亲雪、樣式和Flash組件勇凭。

瀏覽器(和代理)用緩存來減少HTTP請求的數(shù)目和大小,讓頁面能夠更快加載义辕。web服務(wù)器通過有效期HTTP響應(yīng)頭來告訴客戶端虾标,頁面的各個(gè)組件應(yīng)該被緩存多久。用一個(gè)遙遠(yuǎn)的將來時(shí)間做有效期灌砖,告訴瀏覽器這個(gè)響應(yīng)在2010年4月15日前不會(huì)改變璧函。

Expires: Thu, 15 Apr 2010 20:00:00 GMT

如果你用的是Apache服務(wù)器,用ExpiresDefault指令來設(shè)置相對(duì)于當(dāng)前日期的有效期基显。下面的例子設(shè)置了從請求時(shí)間起10年的有效期:

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: &quot;Courier New&quot; !important; font-size: 12px !important;">ExpiresDefault "access plus 10 years"</pre>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蘸吓,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子续镇,更是在濱河造成了極大的恐慌美澳,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異制跟,居然都是意外死亡舅桩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門雨膨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來擂涛,“玉大人,你說我怎么就攤上這事聊记∪雎瑁” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵排监,是天一觀的道長狰右。 經(jīng)常有香客問我,道長舆床,這世上最難降的妖魔是什么棋蚌? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮挨队,結(jié)果婚禮上谷暮,老公的妹妹穿的比我還像新娘。我一直安慰自己盛垦,他們只是感情好湿弦,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著腾夯,像睡著了一般颊埃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蝶俱,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天竟秫,我揣著相機(jī)與錄音,去河邊找鬼跷乐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛趾浅,可吹牛的內(nèi)容都是我干的愕提。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼皿哨,長吁一口氣:“原來是場噩夢啊……” “哼浅侨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起证膨,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤如输,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體不见,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡澳化,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稳吮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缎谷。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖灶似,靈堂內(nèi)的尸體忽然破棺而出列林,到底是詐尸還是另有隱情,我是刑警寧澤酪惭,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布希痴,位于F島的核電站,受9級(jí)特大地震影響春感,放射性物質(zhì)發(fā)生泄漏砌创。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一甥厦、第九天 我趴在偏房一處隱蔽的房頂上張望纺铭。 院中可真熱鬧,春花似錦刀疙、人聲如沸舶赔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽竟纳。三九已至,卻和暖如春疚鲤,著一層夾襖步出監(jiān)牢的瞬間锥累,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工集歇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留桶略,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓诲宇,卻偏偏與公主長得像际歼,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子姑蓝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容