前段優化——雅虎的35個黃金準則

1.儘可能減小HTTP請求數

分類: 內容javascript

80%的終端用戶響應時間都花在了前端上,其中大部分時間都在下載頁面上的各類組件:圖片,樣式表,腳本,Flash等等。減小組件數必然可以減小頁面提交的HTTP請求數。這是讓頁面更快的關鍵。php

減小頁面組件數的一種方式是簡化頁面設計。但有沒有一種方法能夠在構建複雜的頁面同時加快響應時間呢?嗯,確實有魚和熊掌兼得的辦法。css

合併文件是經過把全部腳本放在一個文件中的方式來減小請求數的,固然,也能夠合併全部的CSS。若是各個頁面的腳本和樣式不同的話,合併文件就是一項比較麻煩的工做了,但把這個做爲站點發布過程的一部分確實能夠提升響應時間。html

CSS Sprites是減小圖片請求數量的首選方式。把背景圖片都整合到一張圖片中,而後用CSS的    background-image和    background-position屬性來定位要顯示的部分。前端

圖像映射能夠把多張圖片合併成單張圖片,總大小是同樣的,但減小了請求數並加速了頁面加載。圖片地圖只有在圖像在頁面中連續的時候纔有用,好比導航條。給image map設置座標的過程既無聊又容易出錯,用image map來作導航也不容易,因此不推薦用這種方式。java

行內圖片(Base64編碼)用data:URL模式來把圖片嵌入頁面。這樣會增長HTML文件的大小,把行內圖片放在(緩存的)樣式表中是個好辦法,並且成功避免了頁面變「重」。但目前主流瀏覽器並不能很好地支持行內圖片。node

減小頁面的HTTP請求數是個起點,這是提高站點首次訪問速度的重要指導原則。就像Tenni Theurer的博客Browser Cache Usage – Exposed!裏寫到的,40%到60%的訪客在訪問你的站點時,緩存都是空的。因此,加快頁面首次訪問速度對提升用戶體驗是極其重要的。web


2.使用CDN(Content Delivery Network)

分類: 服務器算法

用戶與服務器的物理距離對響應時間也有影響。把內容部署在多個地理位置分散的服務器上能讓用戶更快地載入頁面。但具體要怎麼作呢?數據庫

實 現內容在地理位置上分散的第一步是:不要嘗試去從新設計你的web應用程序來適應分佈式結構。這取決於應用程序,改變結構可能包括一些讓人望 而生畏的任務,好比同步會話狀態和跨服務器複製數據庫事務(翻譯可能不許確)。縮短用戶和內容之間距離的提議可能被推遲,或者根本不可能經過,就是由於這 個難題。

記住終端用戶80%到90%的響應時間都花在下載頁面組件上了:圖片,樣式,腳本,Flash等等,這是    業績黃金法則。最好先分散靜態內容,而不是一開始就從新設計應用程序結構。這不只可以大大減小響應時間,還更容易表現出CDN的功勞。

內容分發網絡(CDN)是一組分散在不一樣地理位置的web服務器,用來給用戶更高效地發送內容。典型地,選擇用來發送內容的服務器是基於網絡距離的衡量標準的。例如:選跳數(hop)最少的或者響應時間最快的服務器。

一些互聯網公司巨頭擁有他們本身的CDN,但用一個CDN服務提供者是比較划算的,好比Akamai TechnologiesEdgeCast,或者 level3。 對剛剛起步的公司和我的網站來講,CDN服務的成本是很高的,但若是你的用戶羣卻愈來愈大,愈來愈全球化,那麼用CDN來換取更快的響應時間仍是頗有必 要的。在Yahoo!,把靜態內容從應用程序的web服務器搬到CDN(包括上面提到的3rd party和Yahoo本身的CDN)可以提升終端用戶20%甚至更多的響應時間。換到CDN是一個至關簡單的代碼變動,但這將急劇提高站點的響應速度。


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

分類: 服務器

這條規則有兩個方面:

  • 對於靜態組件:經過設置一個遙遠的未來時間做爲Expires來實現永不失效

  • 多餘動態組件:用合適的Cache-ControlHTTP頭來讓瀏覽器進行條件性的請求

網 頁設計愈來愈豐富,這意味着頁面裏有更多的腳本,圖片和Flash。站點的新訪客可能仍是不得不提交幾個HTTP請求,但經過使用有效期能讓組件變得可緩 存,這避免了在接下來的瀏覽過程當中沒必要要的HTTP請求。有效期HTTP頭一般被用在圖片上,但它們應該用在全部組件上,包括腳本、樣式和Flash組 件。

瀏覽器(和代理)用緩存來減小HTTP請求的數目和大小,讓頁面可以更快加載。web服務器經過有效期HTTP響應頭來告訴客戶端,頁面的各個組件應該被緩存多久。用一個遙遠的未來時間作有效期,告訴瀏覽器這個響應在2010年4月15日前不會改變。

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

 

若是你用的是Apache服務器,用ExpiresDefault指令來設置相對於當前日期的有效期。下面的例子設置了從請求時間起10年的有效期:

ExpiresDefault "access plus 10 years"

 

記住,若是你用一個遙遠的將來時間作有效期,就不得不在組件發生變化後及時修改組件的文件名。在Yahoo!,咱們常常把這一步做爲構建過程的一部分:把版本號內嵌在組件的文件名裏,例如:yahoo_2.0.6.js

用一個遙遠的將來時間作有效期HTTP頭,只有在用戶已經訪問過站點以後纔會影響頁面視圖。若是是新訪客或者瀏覽器的緩存被清空時,對HTTP請求的數量並無影響。所以這種性能提高取決於已緩存各個組件的用戶訪問站點的頻率。咱們 在Yahoo!測量了這個數據,發現已緩存各個組件的頁面訪問量(PV)佔75%到85%。經過把一個遙遠的將來時間做爲有效期HTTP頭,增長了被瀏覽器緩存的組件數量,在後續頁面訪問量中不須要用Internet鏈接多發送哪怕一個字節。


4.Gzip組件

分類: 服務器

前端工程師能夠想辦法明顯地縮短經過網絡傳輸HTTP請求和響應的時間。毫無疑問,終端用戶的帶寬速度,網絡服務商,對等交換點的距離等等,都是開發團隊所沒法控制的。但還有別的可以影響響應時間的因素,壓縮能夠經過減小HTTP響應的大小來縮短響應時間。

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

Accept-Encoding: gzip, deflate

 

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

Content-Encoding: gzip

Gzip是目前最多見的高效壓縮方法,由GNU項目開發並被RFC 1952標準化。惟一一個你可能會看到的其它壓縮格式是deflate,但它效率不高並且並不常見。

Gzipping通常可以把響應壓縮到70%左右,目前大約90%的經過瀏覽器的網絡傳輸都支持gzip。若是是Apache服務器,配置gzip的模塊取決於版本:Apache 1.3用mod_gzip而Apache 2.x是 mod_deflate模塊。

瀏覽器和代理的某些因素可能會引發瀏覽器所指望的和它收到的壓縮內容不匹配。幸運的是,隨着老舊瀏覽器的淘汰,這些極少遇到的狀況正在逐漸減小,並且Apache模塊能夠經過自動添加合適的Vary響應頭來幫你搞定。

服務器會根據文件類型來決定要不要用gzip壓縮,但這很是有限。大多數網站都用gzip壓縮HTML文件,其實壓縮腳本,樣式表也是不錯的選 擇,但不少網站卻錯失了這個機會。其實,能夠壓縮任何文本內容,包括XML和JSON,而圖片和PDF不用壓縮,由於它們已經被壓縮過了,再用gzip壓 縮不只浪費CPU還可能會越壓越大。

儘量多地用gzip壓縮可以給頁面減肥,這也是提高用戶體驗最簡單的方法。


5.把樣式表放在頂部

分類: css

在Yahoo!研究性能的時候,咱們發現把樣式表放到文檔的HEAD部分能讓頁面看起來加載地更快。這是由於把樣式表放在head裏能讓頁面逐步渲染。

關注性能的前端工程師想讓頁面逐步渲染。也就是說,咱們想讓瀏覽器儘快顯示已有內容,這在頁面上有一大堆內容或者用戶網速很慢時顯得尤其重要。給用戶顯示反饋(好比進度指標)的重要性已經被普遍研究過,而且被記錄下來了。在咱們的例子中,HTML頁面就是進度指標!當瀏覽器逐漸加載頁面頭部,導航條,頂部logo等等內容的時候,這些都被正在等待頁面加載的用戶看成反饋,可以提升總體用戶體驗。

在不少瀏覽器(包括IE)中,把樣式表放在HTML文檔底部都會阻止頁面逐漸渲染。這些瀏覽器阻塞渲染過程,以免由於樣式變更而重繪頁面元素,用戶這時就只能盯着空白頁面。

HTML官方文檔清楚地描述了樣式表應該放在頁面的HEAD裏面:」Unlike A, [LINK] may only appear in the HEAD section of a document, although it may appear any number of times.」(不像a標籤,link標籤可能只出如今HEAD部分,雖然它能能夠出現任意屢次)。空白屏幕或者沒有樣式的falsh內容都是不可取的。 理想方案就是遵循HTML官方文檔,把樣式表放在HTML文檔的HEAD部分。


6.把腳本放在底部

分類: javascript

腳本會阻塞並行下載,HTTP/1.1官方文檔建議瀏覽器每一個主機名下並行下載的組件數不要超過兩個,若是圖片來自多個主機名,並行下載的數量就能夠超過兩個。若是腳本正在下載,瀏覽器就不開始任何其它下載任務,即便是在不一樣主機名下的。

有時候,並不容易把腳本移動到底部。舉個例子,若是腳本是用    document.write插入到頁面內容中的,就沒辦法再往下移了。還可能存在做用域問題,在多數狀況下,這些問題都是能夠解決的。

一個常見的建議是用推遲(deferred)腳本,有DEFER屬性的腳本意味着不能含有document.write,而且提示瀏覽器告訴他們能夠繼續渲染。不幸的是,Firefox不支持DEFER屬性。在IE中,腳本可能被推遲,但不盡如人意。若是腳本能夠推遲,咱們就能夠把它放到頁面底部,頁面就能夠更快地載入。


7.避免使用CSS表達式

分類: css

用CSS表達式動態設置CSS屬性,是一種強大又危險的方式。從IE5開始支持,但從IE8起就不推薦使用了。例如,能夠用CSS表達式把背景顏色設置成按小時交替的:

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

 

上面的代碼中,expression方法能夠接受一個JavaScript表達式。CSS屬性會被設置成表達式的計算結果。expression方法會被其它瀏覽器忽略,因此只有想辦法實現跨瀏覽器的與IE一致的用戶體驗纔有用。

表達式最大的問題是它們常常被重複計算,比咱們想象的次數還要多。不只僅是頁面渲染和調整大小的時候,在頁面被滾動,甚至用戶在頁面上移動鼠標 時都會從新計算表達式。給CSS表達式添加一個計數器就能夠追蹤它從新計算的時間和頻率,而在頁面上動動鼠標就能夠引起10000屢次從新計算。

減小CSS表達式從新計算的一種方式就是用一次性表達式,即在表達式第一次計算後就把樣式屬性設置成一個明確的值,換掉表達式。若是必需要在頁面的整個生命週期中動態設置樣式屬性,能夠用事件處理器來代替CSS表達式。若是必須使用CSS表達式,要記得它們可能會被重複計算上千次,從而影響整個頁面的性能。


8.把JavaScript和CSS放到外面

分類: javascript, css

不少性能原則都是關於如何管理外部組件的,然而,在這些顧慮出現以前你應該問一個更基礎的問題:應該把JavaScript和CSS放到外部文件中仍是直接寫在頁面裏?

實際上,用外部文件可讓頁面更快,由於JavaScript和CSS文件會被緩存在瀏覽器。HTML文檔中的行內JavaScript和 CSS在每次請求該HTML文檔的時候都會從新下載。這樣作減小了所需的HTTP請求數,但增長了HTML文檔的大小。另外一方面,若是 JavaScript和CSS在外部文件中,而且已經被瀏覽器緩存起來了,那麼咱們就成功地把HTML文檔變小了,並且尚未增長HTTP請求數。

關鍵因素是,外部文件被緩存的頻率和頁面被請求數量之間的關係。儘管這個因素很難量化,但咱們仍是能夠用各類各樣的指標來衡量。若是用戶的每一個會話中都有屢次頁面訪問,那麼相同的腳本和樣式表就能夠被多個頁面複用,緩存的外部文件就會帶來巨大的好處。

不少站點在度量中都處於中等水平,對這些站點來講,通常最好的解決方案就是把JavaScript和CSS部署爲外部文件。惟一的例外是主頁上行內方式優先,例如Yahoo!的首頁My Yahoo!。在每一個會話中訪問量比較少的主頁會發現行內JavaScript和CSS能讓終端用戶的響應時間更快。

對典型的站點來講,首頁是衆多訪問量的開始,有不少技術能夠對減小HTTP請求起到槓桿做用,就像用外部文件緩存的好處同樣。這樣的一種技術就是在首頁用行內JavaScript和CSS,但在頁面載入完成以後動態加載外部文件,這樣後續的頁面所需的外部文件就已經被放到瀏覽器的緩存裏了。


9.減小DNS查找

分類: 內容

域名系統創建了主機名和IP地址間的映射,就像電話簿上人名和號碼的映射同樣。當你在瀏覽器輸入www.yahoo.com的時候,瀏覽器就會聯繫DNS解析器返回服務器的IP地址。DNS是有成本的,它須要20到120毫秒去查找給定主機名的IP地址。在DNS查找完成以前,瀏覽器沒法從主機名下載任何東西。

DNS查找被緩存起來更高效,由用戶的ISP(網絡服務提供商)或者本地網絡存在一個特殊的緩存服務器上,但還能夠緩存在我的用戶的計算機上。 DNS信息被保存在操做系統的DNS cache(微軟Windows上的」DNS客戶端服務」)裏。大多數瀏覽器有獨立於操做系統的本身的cache。只要瀏覽器在本身的cache裏還保留 着這條記錄,它就不會向操做系統查詢DNS。

IE默認緩存DNS查找30分鐘,寫在dnsCacheTimeout註冊表設置中。Firefox緩存1分鐘,能夠用network.dnsCacheExpiration配置項設置。(Fasterfox把緩存時間改爲了1小時 P.S. Fasterfox是FF的一個提速插件)

若是客戶端的DNS cache是空的(包括瀏覽器的和操做系統的),DNS查找數等於頁面上不一樣的主機名數,包括頁面URL,圖片,腳本文件,樣式表,Flash對象等等組件中的主機名,減小不一樣的主機名就能夠減小DNS查找。

減小不一樣主機名的數量同時也減小了頁面可以並行下載的組件數量,避免DNS查找削減了響應時間,而減小並行下載數量卻增長了響應時間。個人原則是把組件分散在2到4個主機名下,這是同時減小DNS查找和容許高併發下載的折中方案。


10.壓縮JavaScript和CSS

分類: javascript, css

壓縮具體來講就是從代碼中去除沒必要要的字符以減小大小,從而提高加載速度。代碼最小化就是去掉全部註釋和沒必要要的空白字符(空格,換行和tab)。在 JavaScript中這樣作可以提升響應性能,由於要下載的文件變小了。兩個最經常使用的JavaScript代碼壓縮工具是 JSMinYUI Compressor,YUI compressor還能夠壓縮CSS。

混淆是一種可選的源碼優化措施,要比壓縮更復雜,因此混淆過程也更容易產生bug。在對美國前十的網站調查中,壓縮能夠縮小21%,而混淆能縮小25%。雖然混淆的縮小程度更高,但比壓縮風險更大。

除了壓縮外部腳本和樣式,行內的 <script><style>塊也能夠壓縮。即便啓用了gzip模塊,先進行壓縮也可以縮小5%或者更多的大小。JavaScript和CSS的用處愈來愈多,因此壓縮代碼會有不錯的效果。


11.避免重定向

分類: 內容

重定向用301和302狀態碼,下面是一個有301狀態碼的HTTP頭:

HTTP/1.1 301 Moved Permanently
      Location: http://example.com/newuri
      Content-Type: text/html


瀏覽器會自動跳轉到Location域指明的URL。重定向須要的全部信息都在HTTP頭部,而響應體通常是空的。其實額外的HTTP頭,好比    ExpiresCache-Control也表示重定向。除此以外還有別的跳轉方式:refresh元標籤和JavaScript,但若是你必須得作重定向,最好用標準的3xxHTTP狀態碼,主要是爲了讓返回按鈕能正常使用。

牢記重定向會拖慢用戶體驗,在用戶和HTML文檔之間插入重定向會延遲頁面上的全部東西,頁面沒法渲染,組件也沒法開始下載,直到HTML文檔被送達瀏覽器。

有一種常見的極其浪費資源的重定向,並且web開發人員通常都意識不到這一點,就是URL尾部缺乏一個斜線的時候。例如,跳轉到    http://astrology.yahoo.com/astrology會返回一個重定向到http://astrology.yahoo.com/astrology/的301響應(注意添在尾部的斜線)。在Apache中能夠用    Alias, mod_rewrite或者 DirectorySlash指令來取消沒必要要的重定向。

重定向最多見的用途是把舊站點鏈接到新的站點,還能夠鏈接同一站點的不一樣部分,針對用戶的不一樣狀況(瀏覽器類型,用戶賬號類型等等)作一些處理。用重定向來鏈接兩個網站是最簡單的,只須要少許的額外代碼。雖然在這些時候使用重定向減小了開發人員的開發複雜度,但下降了用戶體驗。一種替代方案是用 Aliasmod_rewrite,前提是兩個代碼路徑都在相同的服務器上。若是是由於域名變化而使用了重定向,就能夠建立一條CNAME(建立一個指向另外一個域名的DNS記錄做爲別名)結合 Alias或者 mod_rewrite指令。


12.去除重複腳本

分類: javascript

頁面含有重複的腳本文件會影響性能,這可能和你想象的不同。在對美國前10大web站點的評審中,發現只有2個站點含有重複腳本。兩個主要緣由增長了在單一頁面中出現重複腳本的概率:團隊大小和腳本數量。在這種狀況下,重複腳本會建立沒必要要的HTTP請求,執行無用的JavaScript代 碼,而影響頁面性能。

IE會產生沒必要要的HTTP請求,而Firefox不會。在IE中,若是一個不可緩存的外部腳本被頁面引入了兩次,它會在頁面加載時產生兩個HTTP請求。即便腳本是可緩存的,在用戶從新加載頁面時也會產生額外的HTTP請求。

除了產生沒有意義的HTTP請求以外,屢次對腳本求值也會浪費時間。由於不管腳本是否可緩存,在Firefox和IE中都會執行冗餘的JavaScript代碼。

避免不當心把相同腳本引入兩次的一種方法就是在模版系統中實現腳本管理模塊。典型的腳本引入方法就是在HTML頁面中用SCRIPT標籤:

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

 

PHP中一個可選方案是建立一個叫    insertScript的函數:

<?php insertScript("menu.js") ?>

 

除了防止相同腳本被屢次引入,這個函數還能夠解決腳本相關的其它問題,好比依賴性檢查和給腳本文件名添加版本號來支持「永久」有效期HTTP頭。


13.配置ETags

分類: 服務器

實體標籤(ETags),是服務器和瀏覽器用來決定瀏覽器緩存中組件與源服務器中的組件是否匹配的一種機制(「實體」也就是組件:圖片,腳本,樣式表等 等)。添加ETags能夠提供一種實體驗證機制,比最後修改日期更加靈活。一個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傳回源服務器。若是ETags匹配成功,會返回一個304狀態碼,這樣就減小了12195個字節的響應體。

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存在的問題是它們是由特定服務器構造的,因此若是瀏覽器從一個服務器獲取最初的組件,而後想驗證另外一個服務器上的相同組 件,ETags是沒法匹配成功的,而用一羣服務器處理請求在web站點中又很是廣泛。默認狀況下,Apache和IIS會在ETag中嵌入數據,以大大降 低在多服務器站點上有效性測試成功的概率。

Apache 1.3和2.x中ETag的格式是    inode-size-timestamp。就算給定的文件可能在多個服務器的相同目錄下,並且文件大小、訪問權限、時間戳等等所有相同,它的i節點(P.S. inode,UNIX中的索引文件)在不一樣服務器中也不同。

IIS5.0和6.0也都存在相似的問題。IIS中ETags的格式是    Filetimestamp:ChangeNumber,    ChangeNumber是一個用來追蹤IIS配置變動的計數器。 一個站點在不一樣的IIS服務器上的    ChangeNumber是不可能相同的。

最終結果是Apache和IIS爲徹底相同的組件生成的ETags沒法跨瀏覽器匹配,若是ETags不匹配,用戶就沒法收到爲又小又快的304響應設計的 ETags。反而,他們將收到一個攜帶着組件全部數據的200正常響應。若是站點部署在單一服務器上,就根本不存在這個問題。但若是站點部署在多個服務器 上,並且打算用Apache或者IIS的默認ETags配置,用戶將看到緩慢的頁面,服務器負載更高,還會消耗更大的帶寬,而且代理也沒法有效緩存頁面內 容。即便組件有「永久」    ExpiresHTTP頭,用戶點擊從新加載或者刷新的時候,仍然會發出條件GET請求。

若是不想用ETags提供的靈活的驗證模型,最好把全部的Etag全都去掉,能夠用基於組件的時間戳的    Last-ModifiedHTTP頭驗證,並且去掉ETag能夠減小HTTP響應頭以及後續請求的大小.Microsoft Support article裏寫了怎樣移除ETags。在Apache中,能夠簡單地經過在Apache配置文件中添上以下代碼來實現:

FileETag none

 

14.讓Ajax可緩存

分類: 內容

Ajax的一個好處是能夠給用戶提供即時反饋,由於它可以從後臺服務器異步請求信息。然而,用了Ajax就沒法保證用戶在等待異步 JavaScript和XML響應返回期間不會很是無聊。在不少應用程序中,用戶可以一直等待取決於如何使用Ajax。例如,在基於web的電子郵件客戶 端中,用戶爲了尋找符合他們搜索標準的郵件消息,將會保持對Ajax請求返回結果的關注。重要的是,要記得「異步」並不意味着「即時」。

要提升性能,優化這些Ajax響應相當重要。最重要的提升Ajax性能的方法就是讓響應變得可緩存,就像在 添上Expires或者Cache-Control HTTP頭中討論的同樣。下面適用於Ajax的其它規則:

咱們一塊兒看看例子,一個Web 2.0的電子郵件客戶端用了Ajax來下載用戶的通信錄,以便實現自動完成功能。若是用戶從上一次使用以後再沒有修改過她的通信錄,並且Ajax響應是可 緩存的,有還沒有過時的Expires或者Cache-Control HTTP頭,那麼以前的通信錄就能夠從緩存中讀出。必須通知瀏覽器,應該繼續使用以前緩存的通信錄響應,仍是去請求一個新的。能夠經過給通信錄的Ajax URL裏添加一個代表用戶通信錄最後修改時間的時間戳來實現,例如    &t=1190241612。若是通信錄從上一次下載以後再沒有被修改過,時間戳不變,通信錄就將從瀏覽器緩存中直接讀出,從而避免一次額外的HTTP往返消耗。若是用戶已經修改了通信錄,時間戳也能夠確保新的URL不會匹配緩存的響應,瀏覽器將請求新的通信錄條目。

即便Ajax響應是動態建立的,並且可能只適用於單用戶,它們也能夠被緩存,而這樣會讓你的Web 2.0應用更快。


15.儘早清空緩衝區

分類: 服務器

當用戶請求一個頁面時,服務器須要用大約200到500毫秒來組裝HTML頁面,在這期間,瀏覽器閒等着數據到達。PHP中有一個 flush()函數,容許給瀏覽器發送一部分已經準備完畢的HTML響應,以便瀏覽器能夠在後臺準備剩餘部分的同時開始獲取組件,好處主要體如今很忙的後臺或者很「輕」的前端頁面上(P.S. 也就是說,響應時耗主要在後臺方面時最能體現優點)。

比較理想的清空緩衝區的位置是HEAD後面,由於HTML的HEAD部分一般更容易生成,而且容許引入任何CSS和JavaScript文件,這樣就可讓瀏覽器在後臺還在處理的時候就開始並行獲取組件。

例如:

... <!-- css, js -->
    </head>
    <?php flush(); ?>
    <body>
      ... <!-- content -->

 

Yahoo!搜索開創了這項技術,並且真實用戶測試研究也證實了使用這種技術的諸多好處。


16.對Ajax用GET請求

分類: 服務器

Yahoo!郵箱團隊發現使用    XMLHttpRequest時,瀏覽器的POST請求是經過一個兩步的過程來實現的:先發送HTTP頭,在發送數據。因此最好用GET請求,它只須要發送一個TCP報文(除非cookie特別多)。IE的URL長度最大值是2K,因此若是要發送的數據超過2K就沒法使用GET了。

POST請求的一個有趣的反作用是實際上沒有發送任何數據,就像GET請求同樣。正如 HTTP說明文檔中描述的,GET請求是用來檢索信息的。因此它的語義只是用GET請求來請求數據,而不是用來發送須要存儲到服務器的數據。


17.延遲加載組件

分類: 內容

能夠湊近看看頁面並問本身:什麼纔是一開始渲染頁面所必須的?其他內容均可以等會兒。

JavaScript是分隔onload事件以前和以後的一個理想選擇。例如,若是有JavaScript代碼和支持拖放以及動畫的庫,這些都 能夠先等會兒,由於拖放元素是在頁面最初渲染以後的。其它能夠延遲加載的部分包括隱藏內容(在某個交互動做以後纔出現的內容)和摺疊的圖片。

工具可幫你減輕工做量:YUI Image Loader能夠延遲加載摺疊的圖片,還有YUI Get utility是一種引入JS和CSS的簡單方法。Yahoo!主頁就是一個例子,能夠打開Firebug的網絡面板仔細看看。

最好讓性能目標符合其它web開發最佳實踐,好比「漸進加強」。若是客戶端支持JavaScript,能夠提升用戶體驗,但必須確保頁面在不支 持JavaScript時也能正常工做。因此,在肯定頁面運行正常以後,能夠用一些延遲加載腳本加強它,以支持一些拖放和動畫之類的華麗效果。


18.預加載組件

分類: 內容

預加載可能看起來和延遲加載是相反的,但它其實有不一樣的目標。經過預加載組件能夠充分利用瀏覽器空閒的時間來請求未來會用到的組件(圖片,樣式和腳本)。用戶訪問下一頁的時候,大部分組件都已經在緩存裏了,因此在用戶看來頁面會加載得更快。

實際應用中有如下幾種預加載的類型:

  • 無條件預加載——儘快開始加載,獲取一些額外的組件。google.com就是一個sprite圖片預加載的好例子,這個sprite圖片並非google.com主頁須要的,而是搜索結果頁面上的內容。

  • 條件性預加載——根據用戶操做猜想用戶將要跳轉到哪裏並據此預加載。在 search.yahoo.com的輸入框裏鍵入內容後,能夠看到那些額外組件是怎樣請求加載的。

  • 提早預加載——在推出新設計以前預加載。常常在從新設計以後會聽到:「這個新網站不錯,但比之前更慢了」,一部分緣由是用戶訪問先前的頁面都是有舊緩存的,但 新的倒是一種空緩存狀態下的體驗。能夠經過在將要推出新設計以前預加載一些組件來減輕這種負面影響,老站能夠利用瀏覽器空閒的時間來請求那些新站須要的圖 片和腳本。


19.減小DOM元素的數量

分類: 內容

一個複雜的頁面意味着要下載更多的字節,並且用JavaScript訪問DOM也會更慢。舉個例子,想要添加一個事件處理器的時候,循環遍歷頁面上的500個DOM元素和5000個DOM元素是有區別的。

大量的DOM元素是一種徵兆——頁面上有些內容無關的標記須要清理。正在用嵌套表格來佈局嗎?仍是爲了修復佈局問題而添了一堆的 <div>s?或許應該用更好的語義化標記。

YUI CSS utilities對佈局有很大幫助:grids.css針對總體佈局,fonts.css和reset.css能夠用來去除瀏覽器的默認格式。這是個開始清理和思考標記的好機會,例如只在語義上有意義的時候使用    <div>,而不是由於它可以渲染一個新行。

DOM元素的數量很容易測試,只須要在Firebug的控制檯裏輸入:

document.getElementsByTagName('*').length      

那麼多少DOM元素纔算是太多呢?能夠參考其它相似的標記良好的頁面,例如    Yahoo!主頁是一個至關繁忙的頁面,但只有不到700個元素(HTML標籤)。


20.跨域分離組件

分類: 內容

分離組件能夠最大化並行下載,但要確保只用不超過2-4個域,由於存在DNS查找的代價。例如,能夠把HTML和動態內容部署在 www.example.org,而把靜態組件分離到static1.example.orgstatic2.example.org

更多信息請查看Tenni Theurer和Patty Chi的文章:    Maximizing Parallel Downloads in the Carpool Lane 


21.儘可能少用iframe

分類: 內容

用iframe能夠把一個HTML文檔插入到父文檔裏,重要的是明白iframe是如何工做的並高效地使用它。

<iframe>的優勢:

  • 引入緩慢的第三方內容,好比標誌和廣告

  • 安全沙箱

  • 並行下載腳本

<iframe>的缺點:

  • 代價高昂,即便是空白的iframe

  • 阻塞頁面加載

  • 非語義


22.杜絕404

分類: 內容

HTTP請求代價高昂,徹底沒有必要用一個HTTP請求去獲取一個無用的響應(好比404 Not Found),只會拖慢用戶體驗而沒有任何好處。

有些站點用的是有幫助的404——「你的意思是xxx?」,這樣作有利於用戶體驗,,但也浪費了服務器資源(好比數據庫等等)。最糟糕的是連接到的外部JavaScript有錯誤並且結果是404。首先,這種下載將阻塞並行下載。其次,瀏覽器會試圖解析404響應體,由於它是 JavaScript代碼,須要找出其中可用的部分。


23.給Cookie減肥

分類: cookie

使用cookie的緣由有不少,好比受權和個性化。HTTP頭中cookie信息在web服務器和瀏覽器之間交換。重要的是保證cookie儘量的小,以最小化對用戶響應時間的影響。

更多信息請查看Tenni Theurer和Patty Chi的文章:    When the Cookie Crumbles。相關經驗原則能夠總結以下:

  • 清除沒必要要的cookie

  • 保證cookie儘量小,以最小化對用戶響應時間的影響

  • 注意給cookie設置合適的域級別,以避免影響其它子域

  • 設置合適的有效期,更早的有效期或者none能夠更快的刪除cookie,提升用戶響應時間


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

分類: cookie

當瀏覽器發送對靜態圖像的請求時,cookie也會一塊兒發送,而服務器根本不須要這些cookie。因此它們只會形成沒有意義的網絡通訊量,應該確保對靜態組件的請求不含cookie。能夠建立一個子域,把全部的靜態組件都部署在那兒。

若是域名是    www.example.org,能夠把靜態組件部署到    static.example.org。然而,若是已經在頂級域    example.org或者    www.example.org設置了cookie,那麼全部對    static.example.org的請求都會含有這些cookie。這時候能夠再買一個新域名,把全部的靜態組件部署上去,並保持這個新域名不含cookie。Yahoo!用的是    yimg.com,YouTube是    ytimg.com,Amazon是    images-amazon.com等等。

把靜態組件部署在不含cookie的域下還有一個好處是有些代理可能會拒絕緩存帶cookie的組件。有一點須要注意:若是不知道應該用 example.org仍是www.example.org做爲主頁,能夠考慮一下cookie的影響。省略www的話,就只能把cookie寫到   *.example.org,因此由於性能緣由最好用www子域,而且把cookie寫到這個子域下。


25.儘可能減小DOM訪問

分類: javascript

用JavaScript訪問DOM元素是很慢的,因此,爲了讓頁面反應更迅速,應該:

  • 緩存已訪問過的元素的索引

  • 先「離線」更新節點,再把它們添到DOM樹上

  • 避免用JavaScript修復佈局問題

更多信息請查看YUI影院裏Julien Lecomte的文章:    High Performance Ajax Applications 


26.用智能的事件處理器

分類: javascript

有時候感受頁面反映不夠靈敏,是由於有太多頻繁執行的事件處理器被添加到了DOM樹的不一樣元素上,這就是推薦使用    事件委託的緣由。若是一個    div裏面有10個按鈕,應該只給div容器添加一個事件處理器,而不是給每一個按鈕都添加一個。事件可以冒泡,因此能夠捕獲事件並得知哪一個按鈕是事件源。

不須要爲了處理DOM樹而等待onload事件,一般只要目標元素在DOM樹中可訪問便可,而沒必要等待全部的圖片下載完成。能夠考慮用    DOMContentLoaded來代替onload事件,但爲了讓它在全部瀏覽器中均可用,能夠用    YUI Event工具,它有一個    onAvailable方法。

更多信息請查看YUI影院裏Julien Lecomte的文章:    High Performance Ajax Applications 


27.選擇<link>捨棄@import  

分類: css

前面提到了一個最佳實踐:爲了實現逐步渲染,CSS應該放在頂部。

在IE中用  @import  與在底部用    <link>效果同樣,因此最好不要用它。


28.避免使用濾鏡

分類: css

IE專有的 AlphaImageLoader濾鏡能夠用來修復IE7以前的版本中半透明PNG圖片的問題。在圖片加載過程當中,這個濾鏡會阻塞渲染,卡住瀏覽器,還會增長內存消耗並且是被應用到每一個元素的,而不是每一個圖片,因此會存在一大堆問題。

最好的方法是乾脆不要用 AlphaImageLoader,而優雅地降級到用在IE中支持性很好的PNG8圖片來代替。若是非要用    AlphaImageLoader,應該用下劃線hack: _filter來避免影響IE7及更高版本的用戶。


29.優化圖片

分類: 圖片

設計師作好圖片後,在把這些圖片經過FTP上傳到web服務器以前,咱們還能夠作一些事情。

  • 檢查GIF圖片,看看圖片中是否是用了調色板大小對應的顏色數,用 imagemagick能夠簡單地檢查:      
         identify -verbose image.gif      
    若是4色圖片用了調色板中256色的「槽,那就還有改進的餘地

  • 試着把GIF圖片轉換成PNG,看能不能縮減大小,每每能夠。開發者一般不肯意用PNG圖片,由於瀏覽器支持有限,但這都是過去的事情了。真正的問題是 PNG圖片徹底支持alpha透明度,而GIF圖片卻不支持透明度漸變,因此GIF能作的任何事情,PNG均可以(除了動畫)。下面這個簡單的命令就能讓 PNG圖片能夠安全使用:      
         convert image.gif image.png      
    「咱們強調的是:給PNG一個機會。」

  • pngcrush(或者其它的PNG優化工具)處理全部的PNG圖片,例如:      
         pngcrush image.png -rem alla -reduce -brute result.png

  • 用jpegtran處理全部JPEG圖片,這個工具支持對JPEG圖片的無損操做好比旋轉,還能夠用來去除註釋和其它無用信息(好比EXIF信息 P.S. 數碼照片信息,焦距光圈之類的):      
         jpegtran -copy none -optimize -perfect src.jpg dest.jpg

30.優化CSS Sprite

分類: 圖片

  • 在Sprite圖片中橫向排列通常都比縱向排列的最終文件小

  • 組合Sprite圖片中的類似顏色能夠保持低色數,最理想的是256色如下PNG8格式

  • 「對移動端友好」,不要在Sprite圖片中留下太大的空隙。雖然不會在很大程度上影響圖片文件的大小,但這樣作能夠節省用戶代理把圖片解壓成像素映射時消耗的內存。100×100的圖片是1萬個像素,而1000×1000的圖片就是100萬個像素了。


31.不要用HTML縮放圖片

分類: 圖片

不要由於在HTML中能夠設置寬高而使用本不須要的大圖。若是須要

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

那麼圖片自己(mycat.jpg)應該是100x100px的,而不是去縮小500x500px的圖片。


32.用小的可緩存的favicon.ico(P.S. 收藏夾圖標)

分類: 圖片

favicon.ico是放在服務器根目錄的圖片,它會帶來一堆麻煩,由於即使你無論它,瀏覽器也會自動請求它,因此最好不要給一個 404 Not Found響應。並且只要在同一個服務器上,每次請求它時都會發送cookie,此外這個圖片還會干擾下載順序,例如在IE中,當你在onload中請求額外組件時,將會先下載favicon。

因此爲了緩解favicon.ico的缺點,應該確保:

  • 足夠小,最好在1K如下

  • 設置合適的有效期HTTP頭(之後若是想換的話就不能重命名了),把有效期設置爲幾個月後通常比較安全,能夠經過檢查當前favicon.ico的最後修改日期來確保變動能讓瀏覽器知道。

Imagemagick能夠用來處理小收藏夾圖標


33.保證全部組件都小於25K

分類: 移動

這個限制是由於iPhone不能緩存大於25K的組件,注意這裏指的是未壓縮的大小。這就是爲何縮減內容自己也很重要,由於單純的gzip可能不夠。

更多信息請查看Wayne Shea和Tenni Theurer的文章:    Performance Research, Part 5: iPhone Cacheability – Making it Stick 


34.把組件打包到一個複合文檔裏

分類: 移動

把各個組件打包成一個像有附件的電子郵件同樣的複合文檔裏,能夠用一個HTTP請求獲取多個組件(記住一點:HTTP請求是代價高昂的)。用這種方式的時候,要先檢查用戶代理是否支持(iPhone就不支持)。


35.避免圖片src屬性爲空

分類: 服務器

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

  1. straight HTML      

    <img src=」」>

  2. JavaScript      

    var img = new Image();img.src = 「」;

這兩種形式都會引發相同的問題:瀏覽器會向服務器發送另外一個請求。

  • IE向頁面所在目錄發起一個請求

  • Safari和Chrome想當前頁面自己發送一個請求

  • Firefox3及更早版本與Safari和Chrome處理方式同樣,但3.5解決了這個問題 [bug 444931],不會再發送請求了

  • Opera遇到有空src屬性的圖片不作任何處理

爲何圖片src屬性爲空很差?

  1. 意外發送大量的通訊量對服務器來講是很傷的,尤爲是在天天有幾百萬訪問量頁面的時候。

  2. 浪費服務器資源去生成一個根本不可能被看到的頁面

  3. 可能會污染用戶數據,若是追蹤請求狀態,要麼經過cookie要麼是其它方式,可能會破壞用戶數據。即便圖片請求並無返回圖片,整個HTTP頭部也會被瀏覽器接受並讀取,包括全部的cookie。雖然其他部分會被丟棄,但這可能已經形成破壞了。

問題的根本緣由是各個瀏覽器在處理URI時的分歧,這在RFC 3986 – Uniform Resource Identifiers文檔中有明肯定義。若是URI是一個空串,會被看做一個相對URI,並按照5.2節定義的算法處理。實際狀況是,Firefox、 Safari和Chrome都是根據文檔中5.4節列出的規範來處理空串,而IE並無正確處理。聽說在舊版本規範文檔RFC 2396 – Uniform Resource Identifiers(被RFC 3986廢棄了)中,因此從嚴格意義上來講瀏覽器處理相對URI的作法都是對的。問題是,在這種情形下,空串顯然是無意的(P.S. 而不是什麼相對URI)。

HTML5的4.8.2節有關於<img>標籤src屬性的描述,規定瀏覽器再也不發送額外請求:

The src attribute must be present, and must contain a valid URL referencing a non-interactive, optionally animated, image resource that is neither paged nor scripted. If the base URI of the element is the same as the document’s address, then the src attribute’s value must not be the empty string.(P.S. 「src屬性必須存在,並且必須有一個合法的URL引用非交互式的動畫或者圖像資源,不能分頁也不能含有腳本。若是元素的基URI和文檔地址相同,那麼 src屬性的值就不能是空串。」)

但願未來瀏覽器不會存在這個問題,不幸的是,沒有針對<script src=」」>和<link href=」」>條款,也許還有時間調整,以確保瀏覽器不會意外地實現這一行爲。

這條原則是受了Yahoo!的JavaScript大師Nicolas C. Zakas的啓發,更多信息請查看他的文章:    Empty image src can destroy your site  

相關文章
相關標籤/搜索