Web性能優化:What? Why? How?

爲何要提高web性能?css

Web性能黃金準則:只有10%~20%的最終用戶響應時間花在了下載html文檔上,其他的80%~90%時間花在了下載頁面組件上。html

  web性能對於用戶體驗有及其重要的影響,根據著名的`2-5-8`原則:前端

  • 當用戶在2秒之內獲得響應,會感受系統的響應很是快
  • 當用戶在2-5秒以內獲得響應,會感受系統的響應速度還能夠
  • 當用戶在5-8秒以內獲得響應,會感受系統的響應很是慢,但還能夠接受
  • 當用戶在8秒以後都沒有獲得響應,會感受系統糟透了,甚至系統已經掛掉;要麼打開競爭對手的網站,要麼從新發起第二次請求

  凡事都須要研究,經過科學的研究咱們就能夠找到事物的發展規律。這裏要感謝雅虎的工程師總結的14條前端優化法則,使得咱們能夠站在巨人的肩膀上。《高性能網站建設》這本書中的14條優化原則,總結起來主要是如下個方面的優化:java

  1. 減小HTTP請求
  2. 頁面內部優化
  3. 啓用緩存
  4. 減小下載量
  5. 網絡鏈接上的優化

  

爲何減小HTTP請求能夠提升Web性能?web

  要回答這個問題,咱們就要了解當瀏覽器向服務器發送一個http請求知道獲取數據都經歷哪些過程:express

  開啓一個連接(tcp/ip的三次握手過程) -》 發送請求 -》 等待(網絡延遲跟服務器的處理時間)-》 下載數據json

  咱們看一下百度首頁中的http請求在各階段耗費的時間,上面不一樣的顏色表明下圖中的不一樣階段gulp

  能夠看到除了圖片以外,其他大部分http請求的事件花在了創建鏈接與等待階段。segmentfault

  http協議創建在TIC/IP協議之上,在TCP/IP協議中,TCP協議提供可靠的鏈接服務,採用三次握手創建一個鏈接。 簡單來講三次握手就是一個身份確認的過程:windows

  (第一次握手:主機A發送位碼爲syn=1,隨機產生seq number=1234567的數據包到服務器,主機B由SYN=1知道,A要求創建聯機;)

晴兒:你是瀟哥哥嗎,我是晴兒

  (第二次握手:主機B收到請求後要確認聯機信息,向A發送ack number=(主機A的seq+1),syn=1,ack=1,隨機產生seq=7654321的包)

瀟劍:這貨是誰,一簫一劍走江湖,下一句是什麼?

  (第三次握手:主機A收到後檢查ack number是否正確,即第一次發送的seq number+1,以及位碼ack是否爲1,若正確,主機A會再發送ack number=(主機B的seq+1),ack=1,主機B收到後確認seq值與ack=1則鏈接創建成功。)

晴兒:這首詩。。。你真的是瀟哥哥,一蕭一劍走江湖,千古情愁酒一回。。。

瀟劍:晴兒,你真的是晴兒。。。。

(啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪。。。。。。。。。。。。)

  言歸正傳,這個過程也是須要消耗時間的,在百度首頁找到一個極端的例子:

  而等待的時間一般也大於內容下載的時間,這裏一樣找到一個極端例子:

  由此咱們能夠得出結論:一個http請求絕大多數的時間消耗在了創建鏈接跟等待的時間,優化的方法是減小http請求。

 

如何提升web性能?

  一、減小HTTP請求

  通常來講要減小http請求一般從兩個方面下手:減小圖片的請求、減小腳本文件與樣式表的請求

  圖片的減小一般有兩種方式:css sprites、內聯圖片、IconFont。

  CSS Sprites:將多張圖片合併成一幅單獨的圖片,使用css的background-position屬性,將html元素的背景圖片放到sprites 圖片中的指望位置上。使用這項技術的附加優勢是他下降了下載量,合併後的圖片比分離的圖片和更小,由於它下降了圖片自身的開銷(顏色表、格式信息等等)。實際項目中css sprites是一項體力活,由於開發過程當中須要對這張大圖進行維護(添加、減小圖片),張鑫旭同窗的文章中有介紹如何管理sprites圖片能夠做爲參考(這裏)。若是須要在頁面中爲背景、連接、導航欄提供大量的圖片,css sprites絕對是一種優秀的解決方案(乾淨的標籤、較少的圖片、較短的響應時間)。

  內聯圖片:經過使用data:URL模式能夠再頁面中包含圖片而無需任何額外的請求。缺點就是IE8如下的瀏覽器不支持這種方式,而IE8在數據大小上有限制,只能支持23kb之內的數據。對於較小的圖片來講能夠直接內聯到web頁面中,但對於大圖片內聯到頁面裏會致使頁面變大,聰明的作法是使用css,將內聯的圖片做爲背景使用,並放到外部樣式表中,這意味着數據能夠緩存在樣式表內部。使用外部樣式表雖然增長了一個http請求,但樣式能夠被瀏覽器緩存,獲得額外的收穫。另一點須要注意:base64是有損壓縮。

  IconFont:圖標字體,這是近年來新流行的一種以字體代替圖片的技術。它能夠適應任何分辨率而不會出現圖片模糊問題,與圖片相比它具備更小的容量,更高的靈活性(像字體同樣能夠設置圖標大小、顏色、透明度、hover狀態、反轉等),IE8以上的瀏覽器都支持該技術。在使用IconFont以前,你首先要肯定你選則的字體庫是不是收費。詳細內容能夠參考這篇文章:圖標字體化淺談

  減小腳本與樣式表的請求主要原則就是合併。在實際開發中咱們遵循模塊化的原則將代碼分散到許多小文件中,按照軟件開發的原則這是徹底正確的,但對於上線頁面來講,每個文件都會產生一個http請求,嚴重影響性能。和css sprites同樣,將這些小文件合併到一個文件中,能夠減小http請求的數量並縮短最終用戶響應時間。在合併過程當中咱們還須要使用工具精簡(移除沒必要要的字符以減少文件大小縮減下載時間)和混淆(除了移除沒必要要字符外,還會改寫源代碼,好比函數和變量名使用更短的標量名)Javascript代碼。對於採用AMD或CMD進行模塊化開發的同窗,在合併過程當中一般會將依賴的其餘模塊打包到一個文件中,而模板html一般以字符串的方式內聯到Javascript文件中。目前最經常使用的前端構建工具就是glup,這裏有一篇初步應用的文章:前端 | gulp 打包 require.js 模塊依賴

  

  二、頁面內部優化

  關於頁面內部優化主要方向:樣式表放在頂部、腳本文件放在底部、避免css表達式、把腳本的樣式表放在外部、移除重複腳本

  關心性能的工程師都但願頁面可否儘快的展示在用戶面前,對於頁面中不少內容的頁面咱們都但願內容可以逐步加載,爲用戶提供可視化回饋。而將樣式表放在底部會致使瀏覽器阻止內容逐步呈現。爲避免當頁面變化時重繪頁面元素,瀏覽器會阻塞頁面呈現,直到樣式表解析完畢(詳細內容能夠查看個人這篇博客)。因此若是將樣式表放在頂部並不會減小資源的加載時間,它減小的是頁面的呈現時間。小米主頁曾經犯過這樣的錯誤:

  將樣式表放在底部會阻塞頁面的逐步呈現,而將script文件放在頁面頂部一樣會阻塞頁面的逐步呈現。script元素會阻塞後續內容的解析,由於script中能夠同過document.write來更改頁面。解決的辦法就是將script標籤放在頁面底部。這樣既可讓內容逐步呈現,也能夠提升下載的並行度。若是咱們肯定不須要document.write那能夠爲script標籤加上asyn屬性(Ie中要加上defer)提升並行下載度。

  CSS表達式是ie支持的能夠用來動態更改css屬性的一種方式,咱們不須要了解太多,她的書寫方式以下,一旦在產品中發現expression關鍵字就要完全消滅。

  

  使用外部腳本和樣式這一條,我想凡有點經驗的工程師都會這麼幹。

  移除重複腳本:這條說的主要是避免在頁面中屢次加入同一份Javascript代碼,若是咱們的開發中有依賴管理的方式好比AMD、CMD,基本不會出現這種狀況。

 

  三、啓用緩存

  關於緩存的使用這裏介紹兩套方案:expires/If-Modified-Since、Cache-Control/Etag;前者是HTTP1.0中的緩存方案,後者是HTTP1.1中緩存方案,若http頭部中同時出現兩者,後者的優先級更高。

  If-modified-since的方式一般被稱爲條件Get。瀏覽器緩存中保存了一個文件的副本,但須要向服務器詢問此副本是否可用。If-Modified-Since是瀏覽器將最後修改時間發送給服務器,服務器相應頭中Last-Modified進行對比;若If-Modified-Since <= Last-Modified 則瀏覽器讀取本地副本。此時響應狀態爲304 Not Modified, 並不在發送響應體。

  Expries:雖然使用條件GET和304響應可以節省時間,但瀏覽器跟服務器端仍然要發送一次請求進行確認。經過明確設置副本的過時時間能夠避免條件GET。當瀏覽器發現響應頭中的expires時,會將過時時間和文件一塊兒保存到緩存中去。在過時以前一直從緩存中讀取。expires頭使用一個特定的時間來指定緩存的有效期,他要求瀏覽器與服務器時間徹底一致。並且一旦過時,服務器端配置中須要從新設頂一個過時時間。

  ETag(實體標籤):是服務器用於檢查瀏覽器緩存有效性的一種機制。ETag在HTTP1.1中引入,ETag是惟一標識了一個組件的一個特定版本的字符串。惟一的格式約束是這個字符串必須使用雙引號。若是瀏覽器要驗證一個組件是否有效他會使用If-None-Match將etag字符串傳送給服務器。若是ETag是匹配的,服務器端會返回304.(若是實體數據須要根據User-Agent或Accept-Language來改變時,ETag提供了更高的靈活性)。對於使用服務器集羣的網站來講,從一臺服務器到另外一臺服務器,ETag一般是沒法匹配的。這是ETag的問題。並且即使同時使用If-Modified-Since和If-None-Match也並不能達到預期效果。解決方法老是有的:自定義Etag格式

  

  Cache-Control:HTTP1.1引入了來代替Expires,它使用max-age指令來指定副本被緩存多久,該指令以秒爲單位定義了一個更新窗,組件從被請求開始到如今的秒數小於設定值,則一直使用副本。避免了一次http請求。相比Expries,Cache-Control指令提供了更細粒度的控制。詳細內容請看大額同窗的文章:透過瀏覽器看HTTP緩存

 

  四、減小下載量

  減小下載量最有效的方式就是開啓gzip壓縮,gzip是GNU開發的一種免費格式。壓縮組件經過減少http響應的大小來加快響應速度。HTTP1.1經過使用Accept-Encoding來標識支持的壓縮,若是服務器看到這個標識,會使用請求頭中的一種方式來壓縮響應。並經過Content-Encoding來通知web客戶端。不少網站會壓縮html文件,實際上包括xml跟json在內的任何文本均可以壓縮,但圖片和pdf不該該壓縮。根據經驗一般能夠對大於1kb或2kb的文件進行壓縮。壓縮一般能將響應的數據量減小70%。壓縮的成本在於:服務器須要耗費額外的cpu進行壓縮,客戶端須要解壓縮。因此須要在cpu的消耗和數據塊的大小之間進行取捨。

 

  五、優化網絡鏈接

  網絡鏈接的優化主要有三個規則:使用CDN加速、減小DNS查找、避免重定向

  CDN:CDN是地理上分佈的web server的集合,用於更高效地發佈內容。一般基於網絡遠近來選擇給具體用戶服務的web server。 這縮短了資源的傳輸響應時間,有效提升web性能。

  DNS用於映射主機名和IP地址,通常一次解析須要20~120毫秒。瀏覽器會首先根據頁面的主機名進行域名解析,在有ISP返回結果以前頁面不會加載任何內容,因此減小DNS查找能夠有效下降等待時間。爲達到更高的性能,DNS解析一般被多級別地緩存,如由ISP或局域網維護的caching server,本地機器操做系統的緩存(如windows上的DNS Client Service),瀏覽器。IE的缺省DNS緩存時間爲30分鐘,Firefox的缺省緩衝時間是1分鐘。 咱們能作的是儘可能減小一個頁面的主機名,但要在瀏覽器最大並行下載數跟dns查找之間作權衡。根據雅虎的研究,最好將主機名控制在2-4個內。

  重定向:將一個URL從新路由到另外一個URL。重定向功能是經過301和302這兩個HTTP狀態碼完成的,如: 
   HTTP/1.1 301 Moved Permanently 
   Location: http://example.com/newuri 
   Content-Type: text/html 

  瀏覽器自動重定向請求到Location指定的URL上,重定向的主要問題是下降了用戶體驗。 種最耗費資源、常常發生而很容易被忽視的重定向是URL的最後缺乏/,致使自動產生結尾斜線的緣由是,瀏覽器在進行get請求是必須指定一些路徑;若是沒有路徑它就會簡單的使用文檔根。(主機缺乏結尾斜線是不會發生重定向:http://www.baidu.com)缺乏結尾斜線發生重定向是不少web服務器的默認行爲。須要在服務器端設置方可消除。如下圖片是豆瓣的一個url請求:

 

  雅虎的14條優化規則在很長的一段時間裏發揮着重要做用,隨着技術的發展,單單這十四條原則已經不可以知足前端性能優化。在一些大公司出現了前端工程化這一律念,詳細內容能夠參考一下這篇文章:前端性能優化工程化進階

 

  參考資料:

web前端性能意思、關注重點、測試方案、

WEB站點性能優化實踐(加載速度提高2s)

HTTP協議三次握手過程

高性能WEB開發 - 爲何要減小請求數,如何減小請求數!

我是如何對網站CSS進行架構的

圖標字體化淺談

利用ETag緩存優化請求

透過瀏覽器看HTTP緩存

Web應用性能優化黃金法則——轉

http://www.cnblogs.com/dojo-lzz/p/4591446.html

相關文章
相關標籤/搜索