給你的站點全面提速——來自YahooUI的各種BsetPractices

nicenelly發表於2017-11-22

最少化Http請求

終端使用者80%的響應時間都花在前端(而非服務端處理)。而這其中絕大部分的時間又都花在下載所有的頁面“元件”:圖片、樣式表檔案、指令碼檔案、Flash等。而按需載入,減少http的請求數來呈現頁面,是加快頁面呈現的關鍵。

而減少頁面元件的其中一種方式就是簡化頁面設計。但有一種既呈現富客戶端元件,同時又使頁面的響應時間得以加快嗎?這裡有幾種技術能夠有效減少http的請求數,並且又能夠支援富客戶端頁面的設計。

合併檔案:你可以合併多個檔案來減少http請求,比如合併指令碼檔案,簡化併合並多個樣式檔案。當那些指令碼或樣式檔案對各個頁面都不盡相同時,這將變得更具挑戰,但是把它列入你釋出的其中一個步驟吧!

合併圖片:這可以有效地減少你對於圖片的請求。合併你的背景圖片到一個大圖片中,並使用CSS的background-image和background-position屬性來呈現你最終想要的圖片區域。

Image
maps
,合併多個圖片到一個大圖片中。圖片的總體大小和原來相同,但減少了http請求的數量從而可以為網頁加速。Image
maps僅僅在圖片位於頁面中相鄰的區域的情況下,才會起作用,例如實現一個導航條。定義image maps的座標很乏味且容易出錯。使用Image
maps的導航也不可訪問,所以不做過多推薦。

內聯圖片:使用data:URL
scheme
來嵌入圖片的資料到最終的頁面(曾Google有對圖片這個做過)。這會導致你HTML文件的大小變大。合併內聯圖片到你(快取過)的樣式檔案中是一種減少HTTP請求以及避免增加你頁面大小的方式。內聯圖片的方式並不為所有現代主流瀏覽器所主持!

為首次訪問的使用者提高效能,這是一個非常重要的指引。據統計,40%-60%日常使用者訪問你的站點的時候,處於一個“零快取”的狀態。所以,確保你的頁面對那些首次訪問的訪問者來說是快速的,這是一個非常重要的使用者體驗。

使用CDN(ContentDelivery Network)

使用者接近你的web伺服器對於響應時間是有影響的。將你的靜態內容分散到多個不同地理區域的伺服器上,從使用者的角度來看將會覺得你的頁面載入地更迅速。但你應該從哪兒開始呢?

首先作為第一步,你應該實現對內容的地理分散,不要企圖從新設計你的web應用程式來滿足一種“分散式架構”。依賴應用程式,改變架構可能包含著繁重的任務,比如同步Session狀態、跨伺服器區域來複制資料庫事務。企圖減少使用者和你內容的距離可能會被這種應用架構步驟延遲或者,從來都通不過。

終端使用者80%的響應時間都花在前端(而非服務端處理)。而這其中絕大部分的時間又都花在下載所有的頁面“元件”:圖片、樣式表檔案、指令碼檔案、Flash等。這是黃金法則。而不是開始去進行對你係統架構的重新設計等困難的工作。最好先分散你的靜態內容。這不僅會讓響應時間大幅減少,而且也使得內容分發網路變得更加容易。

CDN是一組web伺服器的集合,它跨越多個對使用者更有效的內容分發位置。伺服器對一個特別使用者進行內容分發一般都是基於衡量網路的接近程度。例如,花費最少的網路頻寬或者以最快的時間響應的伺服器將會基於策略被選中。

某些大型的網際網路公司都擁有其自身的CDN,但使用一個CDN服務提供商更具有經濟效益。對於一個剛起步的公司或者一個死人站點,
CDN服務的花費可能是難以接受的,但隨著你的目標使用者群的日趨龐大,甚至全球化,CDN對於加快響應將是必不可少的。在Yahoo,那些將靜態內容移動到CDN上的web應用給終端使用者提升了20%甚至更多的響應時間。選擇CDN對程式碼的改動是輕量級的,並且那將能夠動態地提高你站點的速度。

增加一個Expires或Cache-Control 頭

下面是該規則的兩個方面:

1、 對靜態元件:實現“永不過期”策略(通過設定一個更久遠的Expires頭)

2、 對動態元件:使用Cache-Control響應頭來幫助瀏覽器有條件地請求。

Web頁面設計正在走向富客戶端話,這意味著更多的指令碼、樣式檔案、圖片以及Flash將要放置在頁面上。而第一次訪問你頁面的使用者可能需要發出很多次的http請求,但使用Expires頭可以使某些頁面元件變得“可快取”。這避免了對後續頁面檢視元件的不必要的請求。Expire頭最通常被用於圖片上,但他們應該被使用在所有的包括指令碼、樣式檔案以及Flash元件中。

瀏覽器(以及代理)使用快取來減少http請求的次數,使得網站頁面載入得更快。web伺服器使用Expires的http響應頭來告訴客戶端某個元件應該被快取多久。這是一個更長時間的Expires頭,告訴瀏覽器直到2010年4月15日該響應才會失效。

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

如果你的伺服器是Apache,使用ExpiresDefault指令來設定一個相對當前日期的過期時間。下面的示例演示了使用ExpiresDefault來設定自請求時間起十天之後失效。

 ExpiresDefault "access plus 10 years"

記住,如果你使用一個時間更長的Expires頭,那麼無論何時你改變該元件的時候,你不得不改變該元件的名稱。在Yahoo,我們在該步驟,通常採用如下辦法——使用一個版本號嵌入到元件的名稱中,例如:yahoo_2.0.6.js

使用一個過期時間更長的Expires頭來影響頁面檢視,只是在使用者已經訪問過你的站點後才會起作用。所以當使用者首次訪問或者瀏覽器沒有快取的情況下,它對http請求數沒有產生作用。因此該方法對效能的提升依賴於使用者訪問你的一個擁有初期快取頁面的頻度(初期快取已經包含了所有頁面部件的快取)。通過使用一個更長時間的Expires頭,你可以增加被瀏覽器快取元件的數量,並且可以重用後續頁面檢視而不需要完全請求整個頁面。

Gzip 元件

在網路上傳輸http請求和響應的時間可以隨著前端工程師的正確決定而顯著減少。終端使用者的頻寬速度、Internet服務提供商、對等交換點的接近程度等等都超出了開發團隊的控制,這是事實。但仍然有很多其他對響應時間產生影響的因素。而壓縮通過減少http響應的大小來減少響應時間。

從HTTP1.1開始,web客戶端通過在Accept-Encoding請求頭來標識對壓縮的支援。

 Accept-Encoding: gzip, deflate

如果Web伺服器看到該請求頭,它可能會使用客戶端列出來的其中一種方法來壓縮響應。而web伺服器通知客戶端的方式是在響應頭中標註Content-Encoding:

 Content-Encoding: gzip

Gzip是當下最流行以及最有效的壓縮方式。它由GNU專案組開發並且被標準化到RFC
1952
。你可能會看到的另一種壓縮格式deflate,但它缺失有效性,並且不是特別流行。

Gzip壓縮通常將輸出大小減小到原來的30%。現如今約90%的瀏覽器聲稱支援gzip壓縮。如果你使用Apache,配置gzip的module取決於你的版本,apache1.3使用mod_gzip而apache
2.x使用 mod_deflate。

有可以代理或者瀏覽器期望的內容與壓縮過的內容不匹配,成為一個眾所周知的問題。幸運的是,隨著舊版本瀏覽器的日漸淘汰,這些邊緣問題也正逐漸減少。而Apache能夠自動地幫助你加入Vary響應頭。

伺服器選擇壓縮哪些檔案是基於檔案型別的,但通常對此都有太多的限制。大部分的web站點都gzip它們的html文件。當然你的腳步以及樣式檔案也是值得壓縮的,但許多web站點錯失了這麼做的機會。事實上,任何文字形式的響應,包括XML,JSON都是值得壓縮的。而圖片與PDF檔案不應該被gzip,因為它們已經被壓縮過了。嘗試壓縮它們不僅浪費CPU,而且反而會增加檔案大小。

將樣式表檔案的引用放在頭部

在Yahoo的網站中,通過研究我們發現,將樣式表的引用放到文件的HEAD中能夠使頁面載入得更快。這是因為將樣式表的引用放在HEAD中,允許頁面逐步解析。

前端工程師很關係效能,他們希望頁面被逐步地載入。我們希望瀏覽器任何時候都可以儘可能快地“列印”頁面。這對那些擁有很多內容的頁面以及那些網速很慢的使用者來講是特別重要的。給使用者視覺反饋,比如進度指示,是非常重要的。在這種情況下HTML頁面是一個進度指示器。當瀏覽器逐步地載入頭部、導航條、頭部的logo等。所有的這些都給正在等待頁面的使用者視覺反饋。這能夠提供一個更好地使用者體驗。

將對樣式表的引用放到html文件的底部,這會阻止頁面在很多瀏覽器(包括IE)中的順序呈現。這些瀏覽器會阻塞頁面呈現來防止元素的樣式變化帶來的頁面重繪。這樣使用者在瀏覽的時候將會看到一個空白的頁面。

將指令碼放置到底部

指令碼產生的問題是,他們會阻塞併發的下載。HTTP1.1標準建議瀏覽器對每個主機頁面部件的併發下載數不超過兩個。如果你將你的圖片放在多個不同的域名下,那麼就可以獲得同時不止兩個的併發下載次數。然而,指令碼在下載的時候,瀏覽器卻不會開啟任何其他的下載,甚至來自其他域名的也不會。

在某些情況下,將指令碼移動到尾部並不是那麼容易。例如,指令碼使用document.write來插入頁面內容的某個部分,那麼它就不能被移動到頁面的下面。這可能也是一個“域”的問題,當然有很多方式能夠解決這些問題。

經常出現的一個替代的方案就是延遲指令碼的執行。而defer屬性表示指令碼不包含document.write,它是一個線索——用來告訴瀏覽器,可以繼續渲染。不幸的是,FireFox不支援defer屬性。在IE中,指令碼可能可以被“延遲”,但並不是儘可能地要求指令碼這麼做。如果一個指令碼可以被延遲執行,那麼它也可以被移動到頁面的底部。那將能使你的頁面載入地更快。

避免CSS表示式

CSS表示式很強大(但同時也很危險)。它能夠動態地設定CSS屬性。自從IE5開始被支援,但是在IE8中又開始被廢棄。舉個例子,背景色可以被設定為每個小時改變一次的CSS表示式:

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

就像在這裡展示的一樣,expression方法接受一個javascript表示式。CSS屬性被設定為Javascript表示式計算出的結果。Expression會被其他瀏覽器忽視,所以它只是在IE中需要設定一個持續變化的樣式時才變得很有用。

而採用表示式的問題是,它計算的結果更多時間裡都超過了人們的預期。因為它們不僅在頁面呈現或者改變大小的時候被計算,甚至當頁面被上下滾動或者使用者的滑鼠在頁面上移動的時候也會計算。給CSS表示式加入一個計數器,將會允許我們跟蹤什麼時候或者怎樣使得CSS開始了它的計算。在一個頁面上移動滑鼠可以很容易超過10000求值。

減少你CSS表示式求值次數的一種方式是使用一次表示式,表示式首次計算出結果的時候,將其設定到一個明確的值來替代CSS表示式。如果其在整個頁面的生存期內必須動態地變化樣式,使用事件處理器來取代CSS表示式是一個很不錯的替代方案。如果你必須使用CSS表示式,記住他們可能會被計算成千上萬次,因而可能影響到你頁面的效能。

將Javascript與CSS搬到外部

很多關於提升效能的“準則”都在處理如何管理這些外部“元件”的問題。然而,在這之前,你需要先考慮一個最基本的問題:應該把Javascript與CSS包含到外部檔案中嗎,或者內聯在頁面本身?

總得來說,在現實中使用外部檔案能夠使頁面載入地更快。這是因為javascript與CSS檔案都被快取在瀏覽器中。Javascript與CSS如果內嵌在HTML文件中,那麼每次該文件被請求時,這些js與css都會被再次下載。這能夠減少必要的http請求數,但卻增大了文字本身的大小。在另一方面,如果javascript與css作為外部檔案被快取到瀏覽器或客戶端,html文件的大小會減少同時也沒有增多HTTP請求數。

一個重要的因素是那些外部的JS檔案與CSS檔案元件被快取的頻率也與HTML文件的請求數有關。對於這個因素,雖然難以量化,但可以使用各種各樣的指標來衡量。如果你站點的使用者在一個會話中可以檢視多個頁面檢視並且你的頁面中有很多重用的相同的指令碼以及樣式檔案。這就能明顯地體現從快取外部檔案中獲得的收益。

許多網站在這樣的指標中。對於這些網站而言,通常最好的解決方案是將Javascript與CSS檔案作為外部檔案。而對於這個原則,唯一例外的就是首頁,你可以對它採用內聯的方式。首頁一般只有很少的(也許只有一個)頁面檢視,對一個會話而言,可能會發現內聯javascript與CSS檔案能夠導致終端使用者的響應時間變得更短。

通常對於許多頁面的第一個檢視,有許多技術能夠減少HTTP請求,比如內聯,或者通過分離到外部檔案來從快取獲益。

減少DNS查詢

Domain Name
System(DNS)對映主機名到IP地址,就像電話薄對映人名到他們的電話號碼一樣。當你在瀏覽器位址列中輸入www.yahoo.com的時候,瀏覽器連線著的一個DNS解析器能夠返回伺服器的IP地址。DNS需要一定的時間,通常需要花費20-120毫秒的時間來對一個給定的主機名進行IP地址查詢。除非DNS查詢地址完成,否則瀏覽器不能從該主機下載任何內容。

DNS查詢如果能夠被快取,可以獲得更好的效能。這個快取可以放在一個特殊的快取伺服器上,被使用者的ISP或者本地區域網路來維護,但也有許多快取發生在獨立的使用者計算機上。DNS資訊被儲存在作業系統的DNS快取中(在Windows上是DNS客戶服務)。大部分的瀏覽器都有他們自己的快取,用以區別作業系統的快取。一旦瀏覽器將DNS記錄儲存在它們自己的快取中,它將不需要另外向作業系統請求記錄。

IE預設快取DNS查詢30分鐘,由DnsCacheTimeout登錄檔設定指定。Firefox快取DNS查詢一分鐘,這是由network.dnsCacheExpiration配置設定的。

當客戶端的DNS快取為空(這裡指瀏覽器和作業系統都為空)的時候,DNS查詢的次數就等於網頁上主機名唯一出現的次數。這其中就包含了頁面URL、images、指令碼檔案、樣式檔案、Flash物件等,所使用的URL中的主機名。減少這些唯一URL主機名可以減少DNS的查詢。

減少唯一主機名出現的次數能夠潛在地減少發生在頁面載入時的併發下載次數。避免DNS查詢能夠減少頁面載入的時間,但減少併發載入的次數卻有可能增加響應的時間。我的一個觀點是,將這些元件分割為至少跨越兩個但是最多不超過四個主機。這能夠在兩者之間取得平衡。

壓縮Javascript與CSS

壓縮指的是從程式碼中移除不必要的字元,來減少它的大小以加快響應。當所有不必要的東西(比如空白字元,空格,換行tab製表符等)被移除,檔案就會變得很小。對於JS而言,這提高了響應時間,因為下載檔案的大小小了很多。兩個很流行的壓縮JS的工具是JSMinYUI
Compressor
YUIcompressor也能夠壓縮CSS。

混淆是一種能夠作用在原始碼上的可替代的操作。它比壓縮更為複雜,因而更容易產生錯誤的是混淆本身的步驟。對美國排名前十的網站的一項調查統計,壓縮佔有21%對比混淆佔有25%。儘管混淆有一個更大的壓縮比,但純粹壓縮的風險來得更小一些。

另外,壓縮額外的指令碼和樣式,而內斂到文件中的<script>標籤塊以及<style>樣式快也能夠並且應該被壓縮。甚至如果你gzip你的指令碼和樣式,它們的大小也能夠減少5%或者更多。隨著Javascript以及CSS使用和大小的增加,這將能夠給你提供更好的效能。

避免重定向

重定向都是使用301和302狀態碼完成的。這裡是一個301響應的http頭部:

 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
HTTP狀態碼,這主要是用來確保後退按鈕能夠正常工作。

最主要說明的一點事,重定向降低了使用者體驗。在使用者與HTML文件插入一個重定向延遲了頁面上所有的東西,因為頁面沒有什麼可以呈現並且沒有組建可以開始下載,知道HTML文件到達。

其中一個最沒必要的重定向經常會發生,並且web開發者通常也不會意識到它。它發生在當一個URL結尾需要以一個‘/’結束,但如果缺失的話,就會發生。例如,訪問http://astrology.yahoo.com/astrology導致了一個301響應,其中包含了一個對http://astrology.yahoo.com/astrology/的重定向(注意結尾加了)。在Apache中,通過使用alias或者mod_rewrite或者直接使用DirectorySlash就可以解決,如果你正在使用Apache處理器的話。

移除重複的指令碼

在一個頁面中引入相同的Javascript檔案兩次會損失效能。這不同於你想的那樣。對美國排名前十的網站的一項統計發現,10個站點中有兩個站點包含有重複的指令碼。有兩個主要的因素導致:團隊規模以及指令碼檔案的數目。當發生這樣的情況,重複的指令碼檔案引用會損失效能,因為,它們會建立不必要的HTTP請求並且會完成一些沒必要的執行。

沒必要的HTTP請求會發生在IE中,但不會發生在Firefox中。在IE中,如果一個外部的指令碼被引入兩次。並且沒有快取,它會在頁面載入時產生兩個HTTP請求。就算指令碼被快取,額外的HTTP請求也會在使用者重新載入頁面的時候發生。

除了生成的“不必要”的HTTP請求外,時間也會被浪費在執行這些指令碼上。該冗餘的JS執行在IE與Firefox中都會發生,與這些指令碼是否被快取無關。

避免不小心包含相同的指令碼兩次的一種方法是在你的模板系統中實現一個指令碼關係模組。傳統的包含指令碼的一種方式是在你的HTML頁面中使用SCRIPT標籤。

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

在PHP中,另一種替代方式是建立一個稱之為insertScript的函式。

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

除了防止相同的指令碼被引入多次外,該函式可以處理指令碼的其他問題,比如依賴性檢查以及在指令碼檔案中加入版本號來支援Expires快取頭。

配置Etags

Entity
tags(ETags)是一種供web伺服器與瀏覽器使用,來決定在瀏覽器快取中的元件是否匹配源伺服器元件的方案。(“Entity”在這裡等同於另一個單詞“component”的含義:images,scripts,stylesheets等)。Etags被用來提供一種比last-modified
日期更為靈活的驗證entities的方案。一個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
      If-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都大大降低了對多伺服器站點測試成功的可能性。

對Apache
1.3與2.X來講,ETag的格式是inode-size-timestamp。儘管一個給定的檔案可能位於跨多個伺服器的相同的目錄下,並且他們的大小相同,許可權,建立時間等都相同。但,它的inode卻因不同的伺服器而已。

IIS
5.0與6.0對ETag有一個很簡單的處理方案。ETag在IIS上的格式為Filetimestamp:ChangeNumber。ChangeNumber是一個用來跟蹤對配置改變的計數器。所以,ChangeNumber在跨所有IIS伺服器背後的網站上是不可能相同的。

所以導致的最終結果是,由Apache和IIS對基於相同的頁面元件生成的ETag,在不同的伺服器上是不相同的。如果ETag不匹配,使用者就無法接受到一個為ETag設計的小而快的304響應。取而代之的是,它們將被響應以200狀態碼,同時伴隨著所有元件內容作為響應。如果你只是將你的站點宿主在一個伺服器上,那這將不會產生任何問題。但,如果你採用的是這種多伺服器的部署方式,並且你採用的是Apache或者IIS的預設ETag配置,你的使用者將會獲得更慢的頁面響應,你的伺服器也會隨之帶來更高負荷的負載。你正在使用的更高的頻寬以及那些代理,它們無法有效地快取你的內容。甚至儘管你的元件有一個時間更長的Expires頭,但任何時候,使用者點選重新載入或者重新整理按鈕,仍然會向伺服器發出請求。

如果你沒有利用ETag提供的靈活的驗證模型的優勢,最好就乾脆移除ETag。Last-Modified頭的驗證機制是基於元件的時間戳。ETag的移除減少了HTTP響應頭部與後續的請求頭部的大小。這篇Microsoft
Support
文章介紹瞭如何移除ETag。對Apache伺服器而言,你只需要在你的Apache配置檔案中,簡單地加上下面這句:

 FileETag none

使Aajx可快取

Ajax引入的其中一個好處就是,它能夠對使用者提供即時反饋,因為它從終端伺服器非同步請求資訊。然而,使用Ajax也不能夠保證使用者將不再次按動它手裡的滑鼠來等待非同步的javascript與xml響應的返回。在許多應用中,使用者是否保持等待,取決於怎樣使用Ajax。例如,在一個基於web的郵件客戶端中,使用者將等待Ajax的請求結果,因為他們需要找到所有符合他們搜尋要求的郵件。你需要記住——非同步並不意味著“瞬間”。

對於提高效能,優化Ajax響應是很重要的舉措。提高Ajax效能的最重要的方式是使響應可快取,就像在上面討論的過期頭和快取頭一樣。

讓我們來看一個例子。一個Web
2.0的郵件客戶端,可能會採用Ajax來自動載入使用者的地址簿。如果使用者在距離上一次他使用該郵件客戶端之後都沒有修改過地址簿的話,那麼可以從之前的響應中來讀取地址簿(如果該響應預先被快取過的情況下)。瀏覽器必須要記住,什麼時候使用之前快取過的地址簿,而什麼時候引發一個新請求。可以在非同步請求地址簿的URL上加入一個時間戳來標識使用者修改他地址簿的最後時間,例如,&t=1190241612。如果自從上一次下載之後,地址簿沒有被修改,時間戳將與之前的保持一致,這樣就可以從瀏覽器的快取中直接讀取結果。而如果使用者已經改變過它的地址簿,自從上一次讀取之後。那麼最新的時間戳將能夠確保URL不與快取的URL相匹配。

就算你的Ajax響應是動態建立的,並且只是針對單個使用者的,他們也能夠被快取。這麼做會讓你的Web 2.0應用變得更快。

儘早地重新整理緩衝區

當使用者請求一個頁面,在終端服務“組裝”一個頁面,無論如何最起碼都需要200到500ms。在這段時間中,瀏覽器是閒置的,因為它在等待資料的到達。在PHP中,你有一個flush()函式。它允許你傳送你部分已準備好的HTML響應到瀏覽器端,以在你的服務端還在忙於準備HTML頁面其他部分的時候讓瀏覽器能夠開始下載頁面的部件(js、css檔案)。

放置flush()函式的一個好位置是在HEAD標籤的後面,因為HTML的HEAD很容易生成,並且它允許你包含對CSS檔案以及Javascript檔案的引用。這樣就是服務端仍然在處理,你也可以開始並行地下載這些頁面部件。

例如:

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

對Ajax使用GET請求

Yahoo
Mail團隊發現:當使用XMLHttpRequest時,POST在瀏覽器中被作為兩步來實現:首先傳送頭部,然後才傳送資料。所以,我們最好使用GET請求,它只傳送一次TCP包(除非你的cookies很大)。在IE中,
URL的最大長度為2K,所以如果你傳送超過2K的資料,就不能使用GET。

後載入元件

你可以自己看看你的頁面,並且問你自己——為了呈現頁面,哪些是絕對需要用來做初始化的?這樣,剩餘的內容與元件都可以等到必須的元件載入完之後再載入。

Javascript是分割在onload事件之前和之後載入的理想“侯選人”。例如,如果你有Javascript程式碼或者類庫用來實現拖拽和動畫效果,那它們就屬於可等待的,因為在頁面上“拖拽”元素髮生在初始化呈現之後。可以找到的其他“候選”包括隱藏內容等。

當效能目標內聯著其他web開發的最佳實踐時也是很好的。在這種情況下,漸進增強的想法告訴我們,當瀏覽器支援Javascript時,這能夠提高使用者體驗,但你不得不確信,當Javascript無法執行時,頁面也能夠正常工作。所以,在你已經能夠確信頁面工作地很好以後,你可以加強一些後期載入,給你更多的效能提升,比如拖放動畫等。

預載入元件

預載入可能看起來和後載入相反,但它最終確實有一個不同的目標。通過預載入元件,你可以利用瀏覽器正空閒的時間優勢,來請求你將要使用的部件(比如圖片、樣式檔案或指令碼檔案)。採用這種方式,當使用者訪問下一個頁面,你可能已經下載完大部分的頁面部件並快取在了客戶端,這樣你的頁面將會載入地更快。

其實,這裡有好幾種預載入的方式:

l
無條件預載入—一旦開始載入,你就去載入某些額外的元件(也許暫時你還不需要用到)。檢視一下google.com,可以作為一個示例,看看一個精靈圖片是如何被請求載入的。該精靈圖片其實在google.com的主頁並不需要,但最終肯定會在展示搜尋結果的頁面中使用。

l
有條件預載入—基於使用者的操作,你做出一些有根據的假設。在search.yahoo.com,當你在輸入框中開始輸入的時候,你將看到一些額外的元件是怎樣被載入的。

l
有預計的預載入—在部署一個全新的設計之前可以利用預載入的優勢。這經常發生在一個新版本釋出之後,你可能會聽到——新站點是如此的酷,但它卻比之前的老版本慢得多。其中部分原因是,使用者訪問你的老版本是在一個全快取的環境下,但新版本完全沒有經過快取。你可以在老的站點中,預計性地在瀏覽器空閒的情況下載入一些新站點中需要的圖片或指令碼檔案。

減少DOM元素的個數

一個複雜的頁面意味著要下載更多的資料,也意味著Javascript對DOM的訪問會更慢。如果你在頁面迴圈500或者5000個DOM元素,當你給它們加入事件處理器時,它們確實會產生不同。

很多DOM元素可能會產生一些問題——有些應該提升頁面的標記而不是必要地刪除內容。你是否使用巢狀的table來佈局?你是否為了解決佈局上的問題,向頁面上堆積更多的<div>?很可能有更好地或者在語義上來講更正確的方式來處理你的標籤。

YUI CSS
ulities對於你的佈局能夠提供很大的幫助:grid.css能夠幫助你均衡佈局,fonts.css與reset.css幫你從瀏覽器特性的苦海中解脫出來。這是一次重新開始思考佈局的機會,例如使用<div>只是在當語義上確實需要使用的時候,而不是因為它能夠呈現一個新行。

DOM元素的數目很容易測試,只需要在Firebug的命令列中敲入:

Document.getElementByTagName(‘*’).length

但,多少DOM元素才算太多呢?檢視其他的一些簡單頁面,他們有好的標籤結構。例如:Yahoo! Home Page是一個相當“重量級”
的頁面,但仍然在七百個元素之內(HTML 標籤)。

跨域分割元件

分割元件允許最大化地併發下載。確信你同時使用不超過24個域。例如,你可能將你的HTML文件以及動態內容持有在www.example.org上,然後可以將靜態內容分割到static1.example.org和static2.example.org上。

想獲得更多資訊,請檢視MaximizingParallel Downloads in the Carpool Lane

最小化iframes的數目

Iframe允許一個HTML文件被插入到父文件中。理解iframe如何工作很重要,因為這有助於你有效地使用它:

<iframe>優點:

l 幫助放慢第三方內容比如徽章和廣告

l 安全沙盒

l 併發下載指令碼

<iframe>劣勢:

l 即便是空白頁面也很“昂貴”

l 阻塞頁面的載入

l 沒有語義

沒有404

HTTP 請求很“昂貴”,所以發起一個HTTP請求並獲得一個沒有用的響應(例如404 Not
Found)總是沒有必要的,並且將拖慢使用者的響應時間卻沒有任何好處。

某些站點有幫助性的404——你想XXX?這很好地提高了使用者體驗,但也浪費了服務端資源(比如資料庫等)。特別糟糕的是,當一個連結到外部Javascript的連結是錯誤的並且結果導致了404。首先,該下載將阻塞並行的其他下載,接著如果他是javascript文件,瀏覽器可能會嘗試轉換404響應體,來嘗試發現一些有用的內容。

減少Cookie的大小

HTTP Cookies 通常用於驗證,例如許可權和個性化。c在往返於web伺服器和瀏覽器的HTTP
頭中,ookies的資訊會被交換。保持cookies儘量小很重要,因為它影響使用者的響應時間。

想獲得更多的資訊,請訪問When theCookie Crumbles。記住這些結論:

l 限制不必要的cookies

l 保持cookies儘量小很重要,因為它影響使用者的響應時間

l 要注意,在適當的域級別上設定Cookie,而不對其他子域產生影響

l 設定一個適當的過期日期。一個較早的過期時間或者在不久之後刪除cookie將會提高使用者的響應時間。

為頁面元件使用無Cookie的域名

當瀏覽器對靜態檔案傳送一次請求的同時也攜帶了cookies,而對服務端而言,這些cookies是無用的。所以他們只是增加了網路流量。你應該確保將靜態元件作為無cookie的請求傳送。建立一個子域並且將你所有的靜態元件放在子域裡面。

如果你的域名為www.example.org,你可以將你的靜態元件放在static.example.org上。然而,如果如果你已經在相對於www.example.org來講的頂級域:example.org上設定了cookies,那麼所有static.example.org上將包含那些cookies。在這種情況下,你可以購買一個完整的新域名,來放置你的靜態檔案,並使該域名無cookie。Yahoo!使用的是yimg.com,YouTube使用ytimg.com,Amazon使用images-amazon.com等。

在一個無cookie的域名上放置靜態元件的另一個好處是,有些代理可能會拒絕快取那些帶有cookies的元件。如果你想,你應該對你的主頁使用example.org或者www.example.org。省略www讓你別無選擇,只能將cookies寫到*.example.org,所以,從效能的角度出發,最好使用www.subdomain,將cookie寫到該子域上。

最少化DOM訪問

使用JS訪問DOM元素很慢,所以為了讓頁面有一個更為快速的響應,你應該:

l 取消對已訪問元素的引用

l 更新“離線”的節點,然後將他們加入樹中

l 避免用JS處理佈局問題

想獲得更多的資訊,請閱讀 “High Performance Ajax Applications”

開發靈巧的事件處理器

有時頁面響應不夠迅速,是因為太多的事件處理器掛接在不同的DOM樹的元素上,他們會很頻繁地執行。這就是為什麼使用事件代理是一個很好的解決方案。如果你有10個按鈕在一個div中,附加僅僅一個事件處理器給div包裝器,而不是為每個按鈕都制定一個處理器。事件冒泡機制將能夠使你捕獲到事件以及它起源於哪個按鈕。

為了在DOM樹完成的時候做某些事情,你也不需要等待onload事件。經常,所有你需要的指示你想訪問的元素在DOM樹結構中可以被訪問。你不需要等待所有的圖片被下載完成。DOMContentLoaded是一個你可能值得考慮用來替代onload的事件,但知道它在所有瀏覽器中可採用之前,你可以使用YUI
Event utility,它有一個onAvilable函式。

為了獲得更多的資訊,你可以閱讀這篇文章——“HighPerformance Ajax Applications”

選擇<link>而不是@import

之前的最佳實踐中,其中有一條是——CSS應該在文件的頭部,這樣可以實現逐步呈現。

在IE中@import和在頁面的底部使用<link>的行為相同。所以,最好不要使用它。

避免篩選器

IE專有的AlphaImageLoader篩選器是用來修復在IE版本小於7的瀏覽器中半透明的PNG真彩色問題。該問題使用這個篩選器,當圖片正在被下載的時候,可以用來阻塞圖片的呈現並釋放瀏覽器。它也增加了記憶體的開銷並且被應用到每個元素上面,不是每個圖片,是每個元素。所以,該問題是多方面的。

最好的方案是完全避免對AlphaImageLoader的使用。用PNG8取而代之,他們在IE中表現地很好。如果你一定要用AlphaImageLoader,使用帶下劃線的hack手段——_filter,這就避免了該方法對你的IE7+使用者的“懲罰”。

優化圖片

在前端設計師為你的頁面建立完圖片之後,在你向你的web伺服器請求這些圖片的時候

,這裡仍然有些事情值得你嘗試。

l
你可以檢視GIF圖片,並且看是否他們用的是調色盤的大小對應影像中的顏色數量。使用Imagemagick,可以很容易地檢視使用identify–verbose
image.gif

當你看到一個圖片使用四個顏色以及一個250位顏色的“插槽”在調色盤中,那就表示還有提升的空間。

l
嘗試將GIF圖片格式轉為PNG格式,看是否大小有所減小。大部分情況下,答案都是肯定的。開發者通常都很不情願使用PNG格式的圖片(由於瀏覽器支援受限的問題),但這在現在來講已經是一個過去時了。唯一真正的問題是真彩色的PNG中的Alpha透明度問題,但同樣如此,GIF也不是真彩色,並且也不支援變數的透明度。所以任何一個GIF能夠做到的,一個調色盤PNG(PNG8)也能夠做到(除了動畫)。該簡單的imagemagick命令會完成對一個GIF圖片到PNG格式圖片的“安全”轉換:

Convertimage.gif image.png

“所有我想說的就是——給PNG一次機會”

l 為你所有的PNG圖片執行pngcrush(或者任何其他PNG優化工具)。例如:

Pngcrushimage.png –rem alla –reduce –brute result.pgn

l 給你的JPEG圖片執行jpegtran。該工具能夠對JPEG完成無損優化,例如旋轉等,也能夠用來優化以及移除內容或者其他沒有用的資訊(例如EXIF
資訊)。

優化CSS精靈

l 在精靈中水平地呈現圖片,而不是垂直地呈現,將能夠使檔案更小。

l 在精靈中合併相似的顏色可以使得對顏色計算的消耗降得更低,例如256位的顏色就很適合PNG8。

l
“變得對移動裝置友好”並且不要在精靈中產生很大的差距。這不會體現在檔案的大小上,但這能夠使得需要的記憶體減少掉為使用者代理來比較圖片畫素的開銷。100X100的圖片是1萬個畫素,而1000X1000就是1百萬個畫素。

不要在HTML中延展圖片

不要使用一個比你需要的尺寸更大的圖片,僅僅因為你覺得你可以在HTML中設定寬度和高度。如果你需要

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

然後你的圖片就應該是差不多100*100而不是一個延展到500*500大小的圖片。

確保favicon.ico很小並且可快取

Favicon.ico是一個放置在你伺服器根目錄下的一個圖片。它是必須的,因為如果沒有它,瀏覽器將會一直請求它。所以,最好不要響應以一個404 Not
Found。同時,既然在相同的伺服器上,在每次請求它的時候cookies都會傳送。該圖片也會干擾下載順序,例如在IE中,當你在載入的時候請求一個額外的元件,favicon將會再這些額外的元件之前被載入。

因此,儘量減少favicon.ico帶來的弊端:

l 確保它很小,最好小於1K

l 設定一個你覺得合適的Expires頭。你可以安全地設定Expires頭在未來的幾個月過期。


保持元件在25K以下

該元件的限制是由於,有這樣一個事實——iPhone不會快取超過25K的元件。注意,這是指未壓縮的大小。這樣只採用gzip壓縮可能不是很有效。

打包元件到一個文件中

打包元件到一個文件中就像一個帶有附件的email一樣,它幫助你在一個http請求中獲取幾個元件(記住,HTTP請求是很昂貴的)。當你使用此技術之前,首先核查一下使用者代理是否支援它(iPhone
不支援)。

避免錯誤的圖片引用

Image標籤的空src屬性將帶來不可預期的行為。它有兩種表現形式:

1、 純粹的HTML:

<imgsrc=””>

2、 Javascript

Var img =newImage();

Img.src=””;

這兩種形式都會有相同的影響:瀏覽器將向你的伺服器傳送另一個請求。

l Internet Explorer:向當前頁面所屬的資料夾傳送一個請求

l Safari 以及 Chrome:向最終頁面本身

l FireFox 3以及早期版本的行為和Safari 以及 Chrome行為一致,但3.5版本解決了該問題【bug
444931】並且不再傳送請求

l Opera 不做任何事情

為什麼該做法很不好?

1、 通過傳送大量不可預期的通訊來削弱你伺服器的效能,特別是那些每天被訪問幾百萬此的頁面更是這樣。

2、 浪費伺服器的計算週期來生成一個將不會被顯示的頁面。

3、
可能破壞資料。如果你跟蹤請求的狀態,要麼通過cookies要麼以另一種方式,你將有可能看到破空資料。儘管圖片請求不返回一個圖片,但所有的響應頭被讀取並且被瀏覽器接受,包含所有的cookies。雖然響應的其餘部分被廢棄,但損害可能已經發生。

導致該行為的問題是,在瀏覽器中解析URI的方式不盡相同。該行為被定義在RFC
3986-統一資源標識。當一個空字串被當做一個URI,它被認為是一個相對URI,並且根據定義在5.2節的演算法被解析。該特殊的例子——一個空字串,被列在5.4節。

Firefox, Safari, and Chrome都能夠按照標準正確地解析空字串,但IE並沒有正確地解析,而是選擇了遵循規範的早期版本——RFC
2396(它已經過時,並被3986替代)。所以,從技術角度來講,瀏覽器將以它們自身支援的方式來解析相對URI。該問題在這個上下文中,主要想說明空字串是無意義的。

HTML 5增加了src屬性的描述,指示瀏覽器不作出第4.8.2節中的額外要求:

Src屬性必須被標識,必須包含一個驗證過的URL並引用一個非互動的可選動畫、既不是頁面也不是指令碼的圖片資源。如果元素基本的URI與文件的路徑相同,這樣src屬性的值必須為非空的字串。

值得期待的是,瀏覽器將不會存在這個問題。可能仍然需要一段時間來適應確保瀏覽器不小心執行了該行為。

原文釋出時間為:2012-02-09

本文來自雲棲社群合作伙伴CSDN部落格,瞭解相關資訊可以關注CSDN部落格。


相關文章