本文主要考慮客戶端性能、服務器端和網絡性能,內容框架來自Yahoo Developer Network,包含 7 個類別共 35 條前端性能優化最佳實踐,在此基礎上補充了一些相關或者更符合主流技術的內容。php
前端性能的一個重要指標是頁面加載時間,不只事關用戶體驗,也是搜索引擎排名考慮的一個因素。css
以上數據更說明「加載時間就是金錢」,前端優化主要圍繞提升加載速度進行。html
Web 前端 80% 的響應時間花在圖片、樣式、腳本等資源下載上。最直接的方式是減小頁面所需資源,但並不現實。因此,減小HTTP請求數主要的途徑是:前端
合併JS/CSS文件。服務器端(CDN)自動合併,基於Node.js的文件合併工具,經過把全部腳本放在一個文件中的方式來減小請求數。git
使用CSS Sprite將背景圖片合併成一個文件,經過
background-image
和background-position
控制顯示github
行內圖片(Base64編碼)。使用Data URI scheme將圖片嵌入HTML或者CSS中;或者將CSS、JS、圖片直接嵌入HTML中,會增長文件大小,也可能產生瀏覽器兼容及其餘性能問題。web
減小頁面的HTTP請求數是個起點,這是提高站點首次訪問速度的重要指導原則。算法
用戶輸入URL之後,瀏覽器首先要查詢域名(hostname)對應服務器的IP地址,通常須要耗費20-120毫秒時間。DNS查詢完成以前,瀏覽器沒法從服務器下載任何數據。瀏覽器
基於性能考慮,ISP、局域網、操做系統、瀏覽器都會有相應的DNS緩存機制。緩存
另外減小不一樣的主機名可減小DNS查找,減小不一樣主機名的數量同時也減小了頁面可以並行下載的組件數量,避免DNS查找削減了響應時間,而減小並行下載數量卻增長了響應時間。原則是把組件分散在2到4個主機名下,這是同時減小DNS查找和容許高併發下載的折中方案。
HTTP重定向經過301/302
狀態碼實現。下面是一個有301狀態碼的HTTP頭
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html
複製代碼
瀏覽器會自動跳轉到Location域指明的URL。重定向須要的全部信息都在HTTP頭部,而響應體通常是空的。其實額外的HTTP頭,好比Expires和Cache-Control也表示重定向。除此以外還有別的跳轉方式:refresh元標籤和JavaScript,但若是你必須得作重定向,最好用標準的3xxHTTP狀態碼,主要是爲了讓返回按鈕能正常使用。
客戶端收到服務器的重定向響應後,會根據響應頭中Location的地址再次發送請求。重定向會影響用戶體驗,尤爲是屢次重定向時,用戶在一段時間內看不到任何內容,只看到瀏覽器進度條一直在刷新。
/
但未添加。好比,訪問http://astrology.yahoo.com/astrology
將被301
重定向到 http://astrology.yahoo.com/astrology/
(注意末尾的 /)。若是使用 Apache,能夠經過Alias或mod_rewrite或DirectorySlash解決這個問題。最重要的的優化方式是緩存響應結果。有還沒有過時的Expires或者Cache-Control HTTP頭,那麼以前的資源就能夠從緩存中讀出。必須通知瀏覽器,應該繼續使用以前緩存的資源響應,仍是去請求一個新的。能夠經過給資源的Ajax URL裏添加一個代表用戶資源最後修改時間的時間戳來實現。若是資源從上一次下載以後再沒有被修改過,時間戳不變,資源就將從瀏覽器緩存中直接讀出,從而避免一次額外的HTTP往返消耗。詳見服務器-添加Expires或Cache響應頭
。
頁面初始加載時哪些內容是絕對必需的?不在答案之列的資源均可以延遲加載。好比:
遵循「漸進加強」理念開發的網站:JavaScript用於加強用用戶體驗,但沒有(不支持) JavaScript也能正常工做,徹底能夠延遲加載JavaScript。
將首屏之外的HTML放在不渲染的元素中,如隱藏的
<textarea>
,或者type屬性爲非執行腳本的<script>
標籤中,減小初始渲染的DOM元素數量,提升速度。等首屏加載完成或者用戶操做時,再去渲染剩餘的頁面內容。
預先加載利用瀏覽器空閒時間請求未來要使用的資源,以便用戶訪問下一頁面時更快地響應。
複雜的頁面不只下載的字節更多,JavaScript DOM操做也更慢。例如,同是添加一個事件處理器,500個元素和5000個元素的頁面速度上會有很大區別。
從如下幾個角度考慮移除沒必要要的標記:
<div>
僅爲了處理佈局問題?也許有更好、更語義化的標記。瀏覽器控制檯中輸入如下代碼能夠計算出頁面中有多少 DOM 元素:
document.getElementsByTagName('*').length;
爲何不使用表格佈局?
瀏覽器通常會限制每一個域的並行線程(通常爲6個,甚至更少),使用不一樣的域名能夠最大化下載線程,但注意保持在2-4個域名內,以免DNS查詢損耗。
例如,動態內容放在csspod.com
上,靜態資源放在static.csspod.com
上。這樣還能夠禁用靜態資源域下的Cookie,減小數據傳輸,詳見Cookie 優化
。
用iframe能夠把一個HTML文檔插入到父文檔裏,重要的是明白iframe是如何工做的並高效地使用它。
<iframe>
的優勢:
<iframe>
的缺點:
Iframe 徹底加載之後,父頁面纔會觸發 load 事件。 Safari、Chrome 中經過 JavaScript 動態設置 iframe src 能夠避免這個問題。
HTTP請求很昂貴,返回無效的響應(如404未找到)徹底不必,下降用戶體驗並且毫無益處。 一些網站設計很酷炫、有提示信息的404頁面,有助於提升用戶體驗,但仍是浪費服務器資源。尤爲糟糕的是外部腳本返回404,不只阻塞其餘資源下載,瀏覽器還會嘗試把404頁面內容看成JavaScript解析,消耗更多資源。
用戶與服務器的物理距離對響應時間也有影響。把內容部署在多個地理位置分散的服務器上能讓用戶更快地載入頁面。但具體要怎麼作呢?
網站80-90%響應時間消耗在資源下載上,減小資源下載時間是性能優化的黃金法則。相比分佈式架構的複雜和巨大投入,靜態內容分發網絡(CDN)能夠以較低的投入,得到加載速度有效提高。
內容分發網絡(CDN)是一組分散在不一樣地理位置的web服務器,用來給用戶更高效地發送內容。典型地,選擇用來發送內容的服務器是基於網絡距離的衡量標準的。例如:選跳數(hop)最少的或者響應時間最快的服務器。
Cache-Control頭在HTTP/1.1規範中定義,取代了以前用來定義響應緩存策略的頭(例如 Expires、Pragma)。當前的全部瀏覽器都支持Cache-Control,所以,使用它就夠了。
前端工程師能夠想辦法明顯地縮短經過網絡傳輸HTTP請求和響應的時間。毫無疑問,終端用戶的帶寬速度,網絡服務商,對等交換點的距離等等,都是開發團隊所沒法控制的。但還有別的可以影響響應時間的因素,壓縮能夠經過減小HTTP響應的大小來縮短響應時間。
Gzip壓縮一般能夠減小70%的響應大小,對某些文件更可能高達90%,比Deflate更高效。主流 Web 服務器都有相應模塊,並且絕大多數瀏覽器支持gzip解碼。因此,應該對HTML、CSS、JS、XML、JSON等文本類型的內容啓用壓縮。
注意!!! 圖片和 PDF 文件不要使用 gzip。它們自己已經壓縮過,再使用 gzip 壓縮不只浪費 CPU 資源,並且還可能增長文件體積。
從HTTP/1.1開始,web客戶端就有了支持壓縮的Accept-Encoding HTTP請求頭。
Accept-Encoding: gzip, deflate
若是web服務器看到這個請求頭,它就會用客戶端列出的一種方式來壓縮響應。web服務器經過Content-Encoding響應頭來通知客戶端。
Content-Encoding: gzip
實體標籤(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個字節的響應體。Etag 經過文件版本標識,方便服務器判斷請求的內容是否有更新,若是沒有就響應 304,避免從新下載。
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 複製代碼
用戶請求頁面時,服務器一般須要花費200 ~ 500毫秒來組合 HTML 頁面。在此期間,瀏覽器處於空閒、等待數據狀態。使用PHP中的flush()函數,能夠發送部分已經準備好的 HTML到瀏覽器,以便服務器還在忙於處理剩餘頁面時,瀏覽器能夠提早開始獲取資源。
能夠考慮在</head>
以後輸出一次緩衝,HTML head通常比較容易生成,先發送以便瀏覽器開始獲取<head>
裏引用的CSS等資源。
Example:
<!-- css, js -->
</head>
<?php flush(); ?>
<body>
<!-- content -->
複製代碼
瀏覽器執行XMLHttpRequest POST請求時分紅兩步,先發送Http Header,再發送data。而GET只使用一個TCP數據包(Http Header與data)發送數據,因此首選GET方法。
根據HTTP規範,GET用於獲取數據,POST則用於向服務器發送數據,因此Ajax請求數據時使用GET更符合規範。
圖片src屬性值爲空字符串可能如下面兩種形式出現:
HTML:
<img src="" /> 複製代碼
JavaScript:
var img = new Image(); img.src = ""; 複製代碼
雖然src屬性爲空字符串,但瀏覽器仍然會向服務器發起一個HTTP請求:
空src產生請求的後果不容小覷:
空的href屬性也存在相似問題。用戶點擊空連接時,瀏覽器也會向服務器發送HTTP請求,能夠經過JavaScript阻止空連接的默認的行爲。
Cookie被用於身份認證、個性化設置等諸多用途。Cookie經過HTTP頭在服務器和瀏覽器間來回傳送,減小Cookie大小能夠下降其對響應速度的影響。
靜態資源通常無需使用Cookie,能夠把它們放在使用二級域名或者專門域名的無Cookie服務器上,下降Cookie傳送的形成的流量浪費,提升響應速度。
<head>
中把樣式表放在<head>
中可讓頁面漸進渲染,儘早呈現視覺反饋,給用戶加載速度很快的感受。
這對內容比較多的頁面尤其重要,用戶能夠先查看已經下載渲染的內容,而不是盯着白屏等待。
若是把樣式表放在頁面底部,一些瀏覽器爲減小重繪,會在 CSS 加載完成之後才渲染頁面,用戶只能對着白屏乾瞪眼,用戶體驗極差。把樣式表放到文檔的HEAD部分能讓頁面看起來加載地更快。
CSS表達式能夠在CSS裏執行JavaScript,僅IE5-IE7支持,IE8標準模式已經廢棄。 CSS表達式超出預期的頻繁執行,頁面滾動、鼠標移動時都會不斷執行,帶來很大的性能損耗。
<link>
替代@import
對於IE某些版本,@import的行爲和放在頁面底部同樣。因此,不要用它。
AlphaImageLoader爲IE5.5-IE8專有的技術,和CSS表達式同樣,放進博物館吧。IE專有的AlphaImageLoader濾鏡能夠用來修復IE7以前的版本中半透明PNG圖片的問題。在圖片加載過程當中,這個濾鏡會阻塞渲染,卡住瀏覽器,還會增長內存消耗並且是被應用到每一個元素的,而不是每一個圖片,因此會存在一大堆問題。
注意!!!這裏所說的不是 CSS3 Filter
瀏覽器下載腳本時,會阻塞其餘資源並行下載,即便是來自不一樣域名的資源。所以,最好將腳本放在底部,以提升頁面加載速度。
一些特殊場景沒法將腳本放到頁面底部的,能夠考慮<script>
的如下屬性:
外部JavaScript和CSS文件能夠被瀏覽器緩存,在不一樣頁面間重用,也能下降頁面大小。
固然,實際中也須要考慮代碼的重用程度。若是僅僅是某個頁面使用到的代碼,能夠考慮內嵌在頁面中,減小HTTP請求數。另外,能夠在首頁加載完成之後,預先加載子頁面的資源。
壓縮代碼能夠移除非功能性的字符(註釋、空格、空行等),減小文件大小,提升載入速度。
得益於Node.js的流行,開源社區涌現出許多高效、易用的前端優化工具,JavaScript 和CSS壓縮類的,不敢說多如牛毛,多入雞毛卻是一點不誇張,如[UglifyJS 2] (github.com/mishoo/Ugli…)、csso、cssnano 等。
對於內嵌的CSS和JavaScript,也能夠經過htmlmin等工具壓縮。
這些項目都有Gulp、Webpack等流行構建工具的配套版本。
重複的腳本不只產生沒必要要的HTTP請求,並且重複解析執行浪費時間和計算資源。
JavaScript 操做 DOM 很慢,尤爲是 DOM 節點不少時。
使用時應該注意:
嘗試把GIF格式轉換成PNG格式,看看是否節省空間。在全部的PNG圖片上運行pngcrush(或者其它PNG優化工具)。
YDN列出的相關工具缺少易用性,建議參考如下工具
TODO:
PNG終極優化
不要使用<img>
的width、height縮放圖片,若是用到小圖片,就使用相應大小的圖片。若是須要
<img width="100" height="100" src="mycat.jpg" alt="My Cat" />
那麼圖片自己(mycat.jpg)應該是100x100px的,而不是去縮小500x500px的圖片。
不少 CMS 和 CDN 都提供圖片裁切功能。
補充:設置圖片的寬和高,以避免瀏覽器按照「猜」的寬高給圖片保留的區域和實際寬高差別,產生重繪。
Favicon.ico通常存放在網站根目錄下,不管是否在頁面中設置,瀏覽器都會嘗試請求這個文件。
因此確保這個圖標:
對於較新的瀏覽器,可使用PNG格式的favicon。
這個限制是由於iPhone不能緩存大於25K的組件,注意這裏指的是未壓縮的大小。這就是爲何縮減內容自己也很重要,由於單純的gzip可能不夠。
把各個組件打包成一個像有附件的電子郵件同樣的複合文檔裏,能夠用一個HTTP請求獲取多個組件(記住一點:HTTP請求是代價高昂的)。用這種方式的時候,要先檢查用戶代理是否支持(iPhone就不支持)。
寫到這裏,雅虎的35條軍規算是介紹完了。條目雖然不少,但通過分類,能夠發現,性能優化主要切入點能夠從如下幾個方面去考慮:
所以,要完全掌握優化的方法,必須對http請求的全過程以及瀏覽器的渲染全過程都有深刻的理解。
原文出自本人博客: