Yahoo前端優化性能規則

連接參考: https://developer.yahoo.com/performance/rules.htmljavascript

只有10%~20%的最終用戶響應時間花在了下載HTML文檔上,其他的80%~90%時間花在了下載頁面中的全部組件上。
                                                        ——Steve Souders

規則1——減小HTTP請求(Minimize HTTP Requests)php

只有10%~20%的最終用戶響應時間花在接收請求的HTML文檔上,剩下的80%~90%時間都花在HTML文檔所引用的全部組件(圖片、腳本、樣式表、Flash等)進行的HTTP請求上。所以,改善響應時間最簡單的辦法就是減小組件數量並由此減小HTTP請求數。減小組件數量一般會和產品設計的初衷相矛盾,所以,此處給出了一些技術:

   圖片地圖(Image Maps)聯合多個圖片到一個單獨的圖片中。下載圖片大小的總和保持不變,可是,經過減小HTTP請求數的方式加速了頁面。圖片地圖適用於導航欄或其餘超連接中使用多個圖片的情形。可是,在定義圖片地圖上的區域座標時,若是採用手工方式很難完成且容易出錯,並且除了矩形外幾乎沒法定義其餘形狀。

   CSS Sprites使用CSS background-image和background-position屬性將多個圖片聯合成一個獨立的圖片來顯示。它經過合併圖片減小了HTTP請求,而且比圖片地圖更加靈活,同時也下降了圖片的下載量。若是在頁面中須要爲背景、按鈕、導航欄、連接等提供大量圖片,CSS Sprites是一種優秀的解決方案。

   內聯圖片(Inline images)使用data: URL scheme模式將圖片嵌入到HTML文檔中。經過此模式嵌入圖片,不須要任何額外的HTTP請求開銷。可是,目前的主流瀏覽器(主要是IE)不支持此種方式。

   合併文件(Combined files)經過將全部JavaScript腳本合併到一個文件,全部CSS樣式表合併到另外一個文件的方式來減小HTTP請求的數量。可是簡單的合併一般會遇到模塊化、頁面變化等問題,須要根據頁面引用腳本和樣式表來具體分析以肯定具體的組合方式。

規則2——使用內容發佈網絡(Use a Content Delivery Network)html

用戶同Web服務器的距離會對頁面響應時間產生影響。網站最初一般將其全部服務器放在同一個地方,當用戶羣增長時,公司必須面對服務器放置地點再也不合適的事實。所以,有必要在多個地理位置不一樣的服務器上部署內容。

   做爲實現地理位置分離的第一步,不該當首先嚐試使用分佈式架構從新設計Web應用程序。這樣的應用程序決定了從新設計將帶來如同步會話狀態、在服務器放置地點間複製數據庫事務等複雜問題。從新設計會推遲甚至根本沒法實現縮短用戶和網站內容距離的願望。

   若是應用程序Web服務器裏用戶更近,則一個HTTP請求的響應時間將被縮短;若是組件Web服務器離用戶更近,則多個HTTP請求的響應時間將縮短。所以,與其從新開始設計應用程序,以便將應用程序Web服務器分散開,不如首先將組件Web服務器分散開。這不只能達到響應時間大幅減小的目的,還很容易實現。

   內容發佈網絡(CDN)是一組分佈在多個不一樣地理位置的Web服務器,用於更加有效的向用戶發佈內容。向特定用戶發佈內容的服務器基於對網絡可用度的測量,例如,CDN可能選擇網絡階躍數最小的服務器,或者具備最短響應時間的服務器。

   除了縮短響應時間外,CDN還能夠帶來其餘優點,包括備份、擴展存儲能力和進行緩存;同時,CDN還有助於緩和Web流量峯值的壓力,如在獲取天氣或股市新聞、瀏覽體育或娛樂事件時。依賴CDN的一個缺點是網站的響應時間會受到其餘網站——甚至多是競爭對手流量的影響;另外一個缺點是沒法直接控制組件服務器所帶來的問題。

  CDN用於發佈靜態內容(如圖片、腳本、樣式表、Flash)。提供動態HTML頁面會引入特殊的存儲要求——數據庫鏈接、狀態管理、驗證、硬件和OS優化等,這些複雜性超過了CDN的範圍。另外一方面,靜態文件更容易存儲並具備較少的依賴。

規則3——添加Expires頭(Add an Expires or a Cache-Control Header)java

Web頁面包含大量組件,而且數量在不斷增加。頁面的初訪者會進行不少的HTTP請求,但經過一個長久的Expires頭,可使這些組件被緩存下來,能夠在後續的頁面瀏覽中避免沒必要要的HTTP請求。長久的Expires頭最長用於圖片,但應該將其用於全部組件上,包括腳本、樣式表和Flash。

Web服務器使用Expires頭告訴Web客戶端它可使用一個組件的當前副本,知道指定時間爲止。HTTP規範中簡要的稱該頭爲「在這一日期/時間以後,響應將被認爲是無效的」。例如:

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

告訴瀏覽器該響應的有效性持續到2010年4月15日。

由於Expires頭使用一個特定的時間,它要求服務端和客戶端的時鐘嚴格同步;另外,過時日期須要常常檢查,一旦過時日期到了,須要在服務器中配置提供一個新的日期。因此,HTTP1.1引入了Cache-Control頭來克服Exipres頭的限制。Cache-Control使用max-age指令指定組件被緩存多久,它以秒爲單位定義了一個更新窗。使用帶有max-age的Cache-Control能夠消除Expires的限制,但對於不支持HTTP1.1的應用,仍但願使用Expires頭。能夠同時制定這兩個響應頭,若是二者同時出現時,HTTP規範規定max-age指令將重寫Expires頭。

當出現了Expires頭時,直到過時時間爲止一直會使用緩存的版本,瀏覽器不會檢查任何更新,直到過了過時時間。爲了確保用戶可以獲取組件的最新版本,須要在全部的HTML頁面中修改組件的文件名。Yahoo在此使用了將版本號嵌入在組件的文件名中的方法。

規則4——壓縮組件(Gzip Components)node

壓縮組件經過減小HTTP請求產生的響應包的大小,從而下降傳輸時間的方式來提升性能。從HTTP1.1開始,Web客戶端能夠經過HTTP請求中的Accept-Encoding頭來標識對壓縮的支持:


Accept-Encoding: gzip, deflate

若是Web服務器看到請求中的這個頭,就會使用客戶端列出的方法中的一種來壓縮響應。Web服務器經過響應中的Content-Encoding頭來通知Web客戶端:


Content-Encoding: gzip

目前許多網站一般會壓縮HTML文檔,腳本和樣式表的壓縮也是值得的(包括XML和JSON在內的任何文本響應理論上都值得被壓縮)。可是,圖片和PDF文件不該該被壓縮,由於它們原本已經被壓縮了。

壓縮一般能將響應的數據量減小近70%,可是壓縮一般狀況下會帶來服務端和客戶端的CPU開銷,要檢測受益是否大於開銷,須要綜合考慮響應大小、鏈接的帶寬和客戶端也服務端直接的距離等因素。一般須要對大於1KB或2KB的文件進行壓縮。

當瀏覽器經過代理來發送請求時,有可能出現瀏覽器指望接受的壓縮後內容和實際接收到的不一致的狀況。解決這一問題的方法是在Web服務器的響應中添加Vary頭。Web服務器能夠告訴代理根據一個或多個請求頭來改變緩存的響應。因爲壓縮的決定是基於Accept-Encoding請求頭的,所以須要在服務器的Vary響應頭中包含Accept-Encoding:


Vary: Accept-Encoding

目前大約90%的經過瀏覽器進行的Internet通訊都須要使用gzip,使得服務端和客戶端的對等性變得額外重要。不管是客戶端仍是服務端發送錯誤,都會形成頁面被破壞。避免錯誤的一種方式是採用「瀏覽器白名單」方式,即只爲通過證明支持壓縮的瀏覽器提供壓縮內容,可是當代理緩存加進來之後,處理邊緣情形瀏覽器將變得更加複雜。另外一種方式是使用Vary: *或Cache-Control: private頭來禁用代理緩存。此種方式會爲全部瀏覽器禁用代理緩存,從而增長帶寬開銷。如何平衡壓縮和代理支持須要在加快響應時間、減少帶寬開銷和邊緣情形瀏覽器缺陷之間進行權衡:

若是網站的用戶不多,而且他們處於一個小圈子中,邊緣情形瀏覽器不須要太多關注,能夠壓縮內容並使用Vary: Accept-Encoding。

若是更注重帶寬開銷,能夠和前一種情形同樣,壓縮內容並使用Vary: Accept-Encoding。

若是網站擁有大量的、多變的用戶羣,可以應付較高的帶寬開銷,而且享有高質量的名聲,須要壓縮內容並使用Cache-Control: Private。(Google和Yahoo都使用這種方式)

規則5——將樣式表放在頂部(Put Stylesheets at the Top)數據庫

咱們都但願頁面可以逐步加載,也就是說,咱們但願瀏覽器可以儘快顯示內容。當瀏覽器逐步加載頁面時,頁頭、導航欄、頂端logo等全部這些都會等待頁面的用戶提供視覺反饋,這改善了用戶體驗。將樣式表放在底部,爲避免當樣式變化時重繪頁面中的元素,瀏覽器會阻塞內容逐步呈現。

樣式表在頁面中的位置並不影響下載時間,可是會影響頁面的呈現。根據HTML規範「和A不同,[LINK]只能出如今文檔的HEAD節中,但其出現次數是任意的」。所以,問題的解決方式應該是遵循HTML規範,使用LINK標籤將樣式表放在文檔的HEAD中。

規則6——將腳本放在底部(Put Scripts at the Bottom)瀏覽器

對響應時間影響最大的是頁面中的組件數量,而腳本會阻塞組件的並行下載,帶來性能上的問題。HTTP1.1規範建議瀏覽器從每一個主機名並行下載兩個組件。若是一個Web頁面平均將其組件分別放在兩個主機名下,總體響應時間能夠減小大約一半。咱們能夠經過對瀏覽器默認設置的修改來增長每一個主機名並行下載組件的數量,也可使用CNAME(DNS別名)將組件分別放到多個主機名下。可是,增長並行下載數量一般會帶來性能上的開銷,過多的並行下載有時反而會下降性能。Yahoo!研究代表,使用兩個主機名比使用一、4或10個主機名能帶來更好的性能。

須要咱們注意的是,下載腳本時並行下載其實是被禁用的,即便此時使用了不一樣的主機名,瀏覽器也不會啓動其餘下載。所以,若是將腳本放在頂部,腳本會阻塞後面內容的呈現,也會阻塞後面組件的下載。所以,放置腳本最好的地方是頁面底部,這不會阻止頁面內容呈現,並且頁面中的可視組件能夠儘早下載。

規則7——避免CSS表達式(Avoid CSS Expressions)緩存

CSS表達式是動態設置CSS屬性的一種強大(而且危險)的方式。它從IE5之後的版本被支持,在IE8中已經被廢棄。
表達式的問題在於對其進行的求值頻率比人們指望的要高。它們不僅在呈現頁面和大小改變時求值,當頁面滾動,甚至用戶鼠標在頁面上移過期都要進行求值。

減小CSS表達式求值次數的一種方式是使用一次性表達式,若是CSS表達式必須被求值一次,能夠在這一次中執行重寫它自己。除此以外,還可使用事件處理器來爲特定的事件提供所指望的動態行爲。

規則8——使用外部JavaScript和CSS(Make JavaScript and CSS External)服務器

在現實環境中使用外部文件一般會產生較快的頁面,由於JavaScript和CSS有機會被瀏覽器緩存起來。對於內聯的狀況,因爲HTML文檔一般不會被配置爲能夠進行緩存的,因此每次請求HTML文檔都要下載JavaScript和CSS。因此,若是JavaScript和CSS在外部文件中,瀏覽器能夠緩存它們,HTML文檔的大小會被減小而沒必要增長HTTP請求數量。

決定是否使用外部文件的關鍵在於被緩存的外部文件佔請求的HTML文檔數的比重。若是網站用戶在每次會話中進行屢次頁面訪問,同時頁面重用了多個腳本和樣式表,使用外部文件時很好的選擇。

對於大多數網站而言,難以精確度量以判斷是否使用內聯或外部文件,此時建議是使用外部文件的方式。對於這個問題的一個例外是網站主頁,因爲主頁對於響應時間要求更高,所以更加傾向於內聯而不是外部文件。

對於內聯文件而言,因爲沒法利用瀏覽器緩存,所以給人感受依然比較低效。咱們能夠經過加載後下載和動態內聯的方式來使得網站主頁既能夠得到內聯的優點,同時也能緩存外部文件。

規則9——減小DNS查找(Reduce DNS Lookups)網絡

DNS對於網站來講會帶來開銷。一般瀏覽器查找一個給定主機名的IP要花費20~120毫秒的時間。在DNS查找完成以前,瀏覽器不能今後主機下載任何東西。

DNS查找能夠被緩存起來以提升性能,這種緩存能夠發生在ISP或局域網中的一臺特殊的緩存服務器上,同時,緩存也會發生在獨立的用戶機器上。在用戶請求一個主機名後,DNS信息會留在操做系統的DNS緩存中,大多數瀏覽器也擁有本身的緩存,和操做系統緩存相分離。只要瀏覽器在其緩存中保留了DNS記錄,就不會經過操做系統來請求這個記錄。

當客戶端瀏覽器和操做系統中DNS緩存同時爲空時,DNS查詢的數量等於頁面中惟一主機名的數量,這些主機名包括了頁面的URL、圖片、腳本、樣式表、Flash等。因此,減小惟一主機名數量,能夠減小DNS查詢數。

減小惟一主機名數量會潛在的減小頁面中並行下載的數量。避免DNS查找下降了響應時間,但減小並行下載可能會增長響應時間。對於這種情形,建議將這些組件放在至少2個,但不要超過4個主機名下。

規則10——精簡JavaScript和CSS(Minify JavaScript and CSS)
精簡是從代碼中移除沒必要要的字符以減少其大小,進而改善加載時間。在代碼被精簡後,全部註釋以及沒必要要的空白字符(空格、換行和製表符)都將被移除。對於JavaScript而言,由於須要下載的文件大小減少了,能夠改善響應時間。

混淆是能夠應用在源代碼上的另外一種優化方式。相比較於精簡,混淆更加複雜,所以更容易產生bug。混淆能夠更大程度上壓縮源代碼,可是也存在着必定的風險。

除了外部JavaScript外,內聯在<script>和<style>塊中的源代碼也須要被精簡。即便使用了gzip來壓縮JavaScript和CSS,使用精簡可以將代碼大小再減小5%或者更多。

規則11——避免重定向(Avoid Redirects)

重定向用於將用戶從一個URL路由到另外一個URL。重定向有不少種,其中301和302是最經常使用的兩種。下面是一個301響應頭的示例:


HTTP/1.1 301 Moved Permanently


Location: http://example.com/newuri


Content-Type: text/html

瀏覽器會自動將用戶帶到Location字段給出的URL。重定向所必需的全部信息都包含在這個頭中,響應體一般是空的。無論叫什麼名字,301或者302響應在實際中都不會被緩存,除非有附加的頭(如Expires或Cache-Control等)要求它這麼作。meta refresh標籤和JavaScript也能夠用於重定向,可是最好的技術是使用標準的3xx狀態碼,以保證後退按鈕可以正常工做。

須要咱們記住的是重定向會使頁面變慢。在用戶和HTML文檔間插入一個重定向後,在此HTML文檔到達以前頁面上不會描繪任何東西,任何組件也不會被下載。

有一種重定向最爲浪費,發生的也很頻繁,可是Web開發人員一般都沒有意識到它,它發生在URL的結尾必須出現斜線(/)而沒有出現的情形。例如訪問地址http://astrology.yahoo.com/astrology將致使一個301響應包含重定向至http://astrology.yahoo.com/astrology/。當主機名後缺乏結尾斜線時不會發生重定向。在Apache中,咱們能夠經過Alias指令或者mod_rewrite模塊或者DirectorySlash指令來處理缺乏結尾斜線時的重定向問題。

從一箇舊的站點連接到新的站點是使用重定向的另外一種常見場景。其餘形式還包括將一個網站的不一樣部分鏈接起來,以及基於一些條件(瀏覽器類型、用戶賬戶類型等)來引導用戶。使用重定向來鏈接兩個網站很簡單並且只須要不多的額外代碼。可是,雖然重定向下降了開發的複雜性,也損害了用戶體驗,一般能夠進行其餘的選擇:若是兩個代碼的路徑在同一臺服務器上,可使用Alias和mod_rewrite;若是域名因爲重定向發生改變,可使用一個CNAME(一條DNS記錄,用於建立一個域名指向另外一個域名的別名)讓兩個主機名指向相同的服務器,而後使用Alias和mod_rewrite。

規則12——移除重複腳本(Remove Duplicate Scripts)

在一個頁面中兩次保護同一個JavaScript文件會損傷性能。致使一個腳本重複的因素主要有兩個——團隊大小和腳本數量。

當重複腳本的現象發生時,將產生沒必要要的HTTP請求和浪費執行JavaScript的時間。沒必要要的HTTP請求會發生在IE中,而不會發生在Firefox中。在IE中,若是腳本被包含兩次且沒有被緩存,瀏覽器會在頁面加載期間產生兩個HTTP請求;即便腳本能夠緩存,當用戶從新加載頁面時也會產生額外的HTTP請求。對JavaScript進行的多餘的執行從而浪費時間的現象在IE和Firefox中都存在,與腳本是否被緩存無關。

避免意外包含同一腳本兩次的一種方法是在你的模塊系統中實現一個腳本管理模塊。包含腳本的典型方式是在HTML頁面中使用SCRIPT標籤:


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

另外一種選擇是在PHP中建立一個函數:


<?php insertScript(「menu.js」) ?>

爲了防止統一個腳本被重複添加屢次,insertScript函數須要添加處理腳本的依賴性和版本的功能。

規則13——配置Etag(Configure ETags)

實體標籤(Entity Tag,ETag)是Web服務器和瀏覽器用於確認緩存組件的有效性的一種機制。ETag在HTTP1.1中引入,用於檢測瀏覽器緩存中的組件與原始服務器上的組件是否匹配。ETag是惟一標識了一個組件的一個特定版本字符串。惟一的約束是該字符串必須用引號引發來。原始服務器使用Etag響應頭來指定組件的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傳回原始服務器。若是ETag是匹配的,就會返回304狀態碼,在此例中使響應減小12195字節。

GET /i/yahoo.gif HTTP/1.1


Host: us.yimg.com


f-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT


If-None-Match: 「10c24bc-4ab-457e1c1f」


HTTP/1.1 304 Not Modified

ETag的問題在於,一般使用組件的某些屬性來構造它,這些屬性對於特定的、寄宿了網站的服務器來講是惟一的。當瀏覽器從一臺服務器上獲取了原始組件以後又嘗試向另外一臺服務器來驗證組件時,ETag是不匹配的。這種狀況對於使用服務器集羣來處理請求的網站來講是很常見的一種狀況。默認狀況下,Apache和IIS向ETag中嵌入的數據都會大大下降有效性驗證的成功率。

Apache1.3和2.x的ETag格式是inode-size-timestamp。文件系統使用inode來存儲諸如文件類型、全部者、組和訪問模式等信息。儘管在多臺服務器上一個給定的文件可能位於相同的目錄、具備相同的文件大小、權限、時間戳等,從一臺服務器到另外一臺服務器,器inode仍然是不一樣的。
IIS5.0和6.0在ETag上有着相似的問題。IIS上ETag的格式是Filetimestamp:ChangeNumber。ChangeNumber適用於跟蹤IIS配置變化的計數器。對於一個網站背後的全部IIS服務器來講,ChangeNumber不大可能相同。
最

終的結果是,對於徹底相同的組件,從一臺服務器到另外一臺,Apache和IIS產生的ETag是不會匹配的。若是ETag不匹配,用戶就不會按照ETag的設計計劃那樣接收到更小更快的304響應;相反,它們會收到普通的200響應以及組件的全部數據。若是隻在一臺服務器上部署網站,這一般不會產生問題;但若是使用了服務器集羣,同時使用Apache或者IIS進行默認的ETag配置,用戶響應將變慢,服務器負載將變高,將消耗更多的帶寬,同時代理緩存的效率也會降低。即便組件具備長久的Expires頭,一旦用戶單擊了Reload或Refresh按鈕,依然會產生條件GET請求。
若是組件必須經過最新修改日期以外的一些東西來進行驗證,則ETag是一種強大的方法;若是無須自定義ETag,則最好將其移除。Last-Modified頭基於組件的時間戳進行驗證,能夠提供徹底等價的信息,並且移除ETag能夠減小響應和後續請求的HTTP頭的大小。在Apache中,只要向Apache配置文件中簡單地添加下面一行配置就能移除ETag:
FileETag none

規則14——使Ajax可緩存(Make Ajax Cacheable)

Ajax的一個最重要的優勢就是向用戶提供即時反饋,由於它異步的從後臺Web服務器請求信息。可是,使用Ajax並不保證用戶不會等到異步的JavaScript和XML返回響應。在不少應用程序中,用戶是否須要等待取決於如何使用Ajax。用戶是否須要等待的關鍵因素在於Ajax請求是主動的仍是被動的。主動請求是基於用戶的當前操做而發起的,被動請求則是爲了未來使用而預先發起的。咱們須要注意的是,「異步」並無暗示「實時」。

爲了提高性能,最重要的是優化Ajax響應。而改善這些主動Ajax請求的最重要的方式就是使響應可緩存。如同在「添加Expires頭」中討論的,一些其餘規則也適用於Ajax,包括:壓縮組件、減小DNS查找、精簡JavaScript、避免重定向、配置Etag。
相關文章
相關標籤/搜索