前端性能優化之雅虎35條軍規

本文主要考慮客戶端性能、服務器端和網絡性能,內容框架來自Yahoo Developer Network,包含 7 個類別共 35 條前端性能優化最佳實踐,在此基礎上補充了一些相關或者更符合主流技術的內容。php

前端性能優化

前端性能的一個重要指標是頁面加載時間,不只事關用戶體驗,也是搜索引擎排名考慮的一個因素。css

  • 來自Google的數據代表,一個有10條數據0.4秒能加載完的頁面,變成30條數據0.9秒加載完以後,流量和廣告收入降低90%。
  • Google Map 首頁文件大小從100KB減少到70-80KB後,流量在第一週漲了10%,接下來的三週漲了25%。
  • 亞馬遜的數據代表:加載時間增長100毫秒,銷量就降低1%。

以上數據更說明「加載時間就是金錢」,前端優化主要圍繞提升加載速度進行。html

1、頁面內容

(1)減小HTTP請求數

Web 前端 80% 的響應時間花在圖片、樣式、腳本等資源下載上。最直接的方式是減小頁面所需資源,但並不現實。因此,減小HTTP請求數主要的途徑是:前端

合併JS/CSS文件。服務器端(CDN)自動合併,基於Node.js的文件合併工具,經過把全部腳本放在一個文件中的方式來減小請求數。git

使用CSS Sprite將背景圖片合併成一個文件,經過background-imagebackground-position 控制顯示github

行內圖片(Base64編碼)。使用Data URI scheme將圖片嵌入HTML或者CSS中;或者將CSS、JS、圖片直接嵌入HTML中,會增長文件大小,也可能產生瀏覽器兼容及其餘性能問題。web

減小頁面的HTTP請求數是個起點,這是提高站點首次訪問速度的重要指導原則。算法

(2)減小DNS查詢

用戶輸入URL之後,瀏覽器首先要查詢域名(hostname)對應服務器的IP地址,通常須要耗費20-120毫秒時間。DNS查詢完成以前,瀏覽器沒法從服務器下載任何數據。瀏覽器

基於性能考慮,ISP、局域網、操做系統、瀏覽器都會有相應的DNS緩存機制。緩存

  • IE緩存30分鐘,能夠經過註冊表中DnsCacheTimeout項設置;
  • Firefox緩存1分鐘,經過network.dnsCacheExpiration配置;

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

(3)避免重定向

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的地址再次發送請求。重定向會影響用戶體驗,尤爲是屢次重定向時,用戶在一段時間內看不到任何內容,只看到瀏覽器進度條一直在刷新。

  • 最浪費的重定向常常發生、並且很容易被忽略:URL 末尾應該添加/但未添加。好比,訪問http://astrology.yahoo.com/astrology將被301重定向到 http://astrology.yahoo.com/astrology/(注意末尾的 /)。若是使用 Apache,能夠經過Alias或mod_rewrite或DirectorySlash解決這個問題。
  • 網站域名變動:CNAME結合Alias或mod_rewrite或者其餘服務器相似功能實現跳轉。

(4)緩存Ajax請求

最重要的的優化方式是緩存響應結果。有還沒有過時的Expires或者Cache-Control HTTP頭,那麼以前的資源就能夠從緩存中讀出。必須通知瀏覽器,應該繼續使用以前緩存的資源響應,仍是去請求一個新的。能夠經過給資源的Ajax URL裏添加一個代表用戶資源最後修改時間的時間戳來實現。若是資源從上一次下載以後再沒有被修改過,時間戳不變,資源就將從瀏覽器緩存中直接讀出,從而避免一次額外的HTTP往返消耗。詳見服務器-添加Expires或Cache響應頭

(5)延遲加載

頁面初始加載時哪些內容是絕對必需的?不在答案之列的資源均可以延遲加載。好比:

  • 非首屏使用的數據、樣式、腳本、圖片等;
  • 用戶交互時纔會顯示的內容。

遵循「漸進加強」理念開發的網站:JavaScript用於加強用用戶體驗,但沒有(不支持) JavaScript也能正常工做,徹底能夠延遲加載JavaScript。

將首屏之外的HTML放在不渲染的元素中,如隱藏的<textarea>,或者type屬性爲非執行腳本的<script>標籤中,減小初始渲染的DOM元素數量,提升速度。等首屏加載完成或者用戶操做時,再去渲染剩餘的頁面內容。

(6)預加載

預先加載利用瀏覽器空閒時間請求未來要使用的資源,以便用戶訪問下一頁面時更快地響應。

  • 無條件預先加載:頁面加載完成(load)後,立刻獲取其餘資源。以 google.com 爲例,首頁加載完成後會當即下載一個 Sprite 圖片,此圖首頁不須要,可是搜索結果頁要用到。
  • 有條件預先加載:根據用戶行爲預判用戶去向,預載相關資源。好比search.yahoo.com開始輸入時會有額外的資源加載。Chrome 等瀏覽器的地址欄也有相似的機制。
  • 有「陰謀」的預先加載:頁面即將上線新版前預先加載新版內容。網站改版後因爲緩存、使用習慣等緣由,會有舊版的網站更快更流暢的反饋。爲緩解這一問題,在新版上線以前,舊版能夠利用空閒提早加載一些新版的資源緩存到客戶端,以便新版正式上線後更快的載入。

(7)減小DOM元素數量

複雜的頁面不只下載的字節更多,JavaScript DOM操做也更慢。例如,同是添加一個事件處理器,500個元素和5000個元素的頁面速度上會有很大區別。

從如下幾個角度考慮移除沒必要要的標記:

  • 是否還在使用表格佈局?
  • 塞進去更多的<div>僅爲了處理佈局問題?也許有更好、更語義化的標記。
  • 能經過僞元素實現的功能,就不必添加額外元素,如清除浮動。

瀏覽器控制檯中輸入如下代碼能夠計算出頁面中有多少 DOM 元素:

document.getElementsByTagName('*').length;

爲何不使用表格佈局?

  • 更多的標籤,增長文件大小;
  • 不易維護,沒法適應響應式設計;
  • 性能考量,默認的表格佈局算法會產生大量重繪

(8)劃份內容到不一樣域名

瀏覽器通常會限制每一個域的並行線程(通常爲6個,甚至更少),使用不一樣的域名能夠最大化下載線程,但注意保持在2-4個域名內,以免DNS查詢損耗。

例如,動態內容放在csspod.com上,靜態資源放在static.csspod.com上。這樣還能夠禁用靜態資源域下的Cookie,減小數據傳輸,詳見Cookie 優化

(9)儘可能減小iframe的使用

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

<iframe>的優勢:

  • 能夠用來加載速度較慢的第三方資源,如廣告、徽章;
  • 可用做安全沙箱;
  • 能夠並行下載腳本。

<iframe>的缺點:

  • 加載代價昂貴,即便是空的頁面;
  • 阻塞頁面 load 事件觸發;

Iframe 徹底加載之後,父頁面纔會觸發 load 事件。 Safari、Chrome 中經過 JavaScript 動態設置 iframe src 能夠避免這個問題。

  • 缺少語義。

(10)避免404錯誤

HTTP請求很昂貴,返回無效的響應(如404未找到)徹底不必,下降用戶體驗並且毫無益處。 一些網站設計很酷炫、有提示信息的404頁面,有助於提升用戶體驗,但仍是浪費服務器資源。尤爲糟糕的是外部腳本返回404,不只阻塞其餘資源下載,瀏覽器還會嘗試把404頁面內容看成JavaScript解析,消耗更多資源。

2、服務器

(1)使用CDN

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

網站80-90%響應時間消耗在資源下載上,減小資源下載時間是性能優化的黃金法則。相比分佈式架構的複雜和巨大投入,靜態內容分發網絡(CDN)能夠以較低的投入,得到加載速度有效提高。

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

(2)添加Expires或Cache-Control響應頭

  • 靜態內容:將 Expires 響應頭設置爲未來很遠的時間,實現「永不過時」策略;
  • 動態內容:設置合適的 Cache-Control 響應頭,讓瀏覽器有條件地發起請求。

Cache-Control頭在HTTP/1.1規範中定義,取代了以前用來定義響應緩存策略的頭(例如 Expires、Pragma)。當前的全部瀏覽器都支持Cache-Control,所以,使用它就夠了。

(3)啓用Gzip

前端工程師能夠想辦法明顯地縮短經過網絡傳輸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

(4)配置 Etag

實體標籤(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
複製代碼

(5)儘早輸出(flush)緩衝

用戶請求頁面時,服務器一般須要花費200 ~ 500毫秒來組合 HTML 頁面。在此期間,瀏覽器處於空閒、等待數據狀態。使用PHP中的flush()函數,能夠發送部分已經準備好的 HTML到瀏覽器,以便服務器還在忙於處理剩餘頁面時,瀏覽器能夠提早開始獲取資源。

能夠考慮在</head>以後輸出一次緩衝,HTML head通常比較容易生成,先發送以便瀏覽器開始獲取<head>裏引用的CSS等資源。

Example:

<!-- css, js -->
</head>
<?php flush(); ?>
<body>
<!-- content -->
複製代碼

(6)Ajax請求使用GET方法

瀏覽器執行XMLHttpRequest POST請求時分紅兩步,先發送Http Header,再發送data。而GET只使用一個TCP數據包(Http Header與data)發送數據,因此首選GET方法。

根據HTTP規範,GET用於獲取數據,POST則用於向服務器發送數據,因此Ajax請求數據時使用GET更符合規範。

(7)避免圖片src爲空

圖片src屬性值爲空字符串可能如下面兩種形式出現:

HTML:

<img src="" />
複製代碼

JavaScript:

var img = new Image(); 
img.src = "";
複製代碼

雖然src屬性爲空字符串,但瀏覽器仍然會向服務器發起一個HTTP請求:

  • IE 向頁面所在的目錄發送請求;
  • Safari、Chrome、Firefox向頁面自己發送請求;
  • Opera不執行任何操做。

空src產生請求的後果不容小覷:

  • 給服務器形成意外的流量負擔,尤爲時日 PV 較大時;
  • 浪費服務器計算資源;
  • 可能產生報錯。

空的href屬性也存在相似問題。用戶點擊空連接時,瀏覽器也會向服務器發送HTTP請求,能夠經過JavaScript阻止空連接的默認的行爲。

3、Cookie

(1)減小 Cookie 大小

Cookie被用於身份認證、個性化設置等諸多用途。Cookie經過HTTP頭在服務器和瀏覽器間來回傳送,減小Cookie大小能夠下降其對響應速度的影響。

  • 去除沒必要要的 Cookie;
  • 儘可能壓縮 Cookie 大小;
  • 注意設置 Cookie 的 domain 級別,如無必要,不要影響到 sub-domain;
  • 設置合適的過時時間。

(2)靜態資源使用無Cookie域名

靜態資源通常無需使用Cookie,能夠把它們放在使用二級域名或者專門域名的無Cookie服務器上,下降Cookie傳送的形成的流量浪費,提升響應速度。

4、CSS

(1)把樣式表放在<head>

把樣式表放在<head>中可讓頁面漸進渲染,儘早呈現視覺反饋,給用戶加載速度很快的感受。

這對內容比較多的頁面尤其重要,用戶能夠先查看已經下載渲染的內容,而不是盯着白屏等待。

若是把樣式表放在頁面底部,一些瀏覽器爲減小重繪,會在 CSS 加載完成之後才渲染頁面,用戶只能對着白屏乾瞪眼,用戶體驗極差。把樣式表放到文檔的HEAD部分能讓頁面看起來加載地更快。

(2)不要使用CSS表達式

CSS表達式能夠在CSS裏執行JavaScript,僅IE5-IE7支持,IE8標準模式已經廢棄。 CSS表達式超出預期的頻繁執行,頁面滾動、鼠標移動時都會不斷執行,帶來很大的性能損耗。

(3)使用<link>替代@import

對於IE某些版本,@import的行爲和放在頁面底部同樣。因此,不要用它。

(4)不要使用 filter

AlphaImageLoader爲IE5.5-IE8專有的技術,和CSS表達式同樣,放進博物館吧。IE專有的AlphaImageLoader濾鏡能夠用來修復IE7以前的版本中半透明PNG圖片的問題。在圖片加載過程當中,這個濾鏡會阻塞渲染,卡住瀏覽器,還會增長內存消耗並且是被應用到每一個元素的,而不是每一個圖片,因此會存在一大堆問題。

注意!!!這裏所說的不是 CSS3 Filter

5、Javasript

(1)把腳本放在頁面底部

瀏覽器下載腳本時,會阻塞其餘資源並行下載,即便是來自不一樣域名的資源。所以,最好將腳本放在底部,以提升頁面加載速度。

一些特殊場景沒法將腳本放到頁面底部的,能夠考慮<script>的如下屬性:

  • defer 屬性;
  • HTML5 新增的async屬性。

(2)使用外部JavaScript和CSS

外部JavaScript和CSS文件能夠被瀏覽器緩存,在不一樣頁面間重用,也能下降頁面大小。

固然,實際中也須要考慮代碼的重用程度。若是僅僅是某個頁面使用到的代碼,能夠考慮內嵌在頁面中,減小HTTP請求數。另外,能夠在首頁加載完成之後,預先加載子頁面的資源。

(3)壓縮JavaScript和CSS

壓縮代碼能夠移除非功能性的字符(註釋、空格、空行等),減小文件大小,提升載入速度。

得益於Node.js的流行,開源社區涌現出許多高效、易用的前端優化工具,JavaScript 和CSS壓縮類的,不敢說多如牛毛,多入雞毛卻是一點不誇張,如[UglifyJS 2] (github.com/mishoo/Ugli…)、csso、cssnano 等。

對於內嵌的CSS和JavaScript,也能夠經過htmlmin等工具壓縮。

這些項目都有Gulp、Webpack等流行構建工具的配套版本。

(4)移除重複腳本

重複的腳本不只產生沒必要要的HTTP請求,並且重複解析執行浪費時間和計算資源。

(5)減小DOM操做

JavaScript 操做 DOM 很慢,尤爲是 DOM 節點不少時。

使用時應該注意:

  • 緩存已經訪問過的元素;
  • 使用DocumentFragment暫存DOM,整理好之後再插入DOM樹;
  • 操做className,而不是屢次讀寫style;
  • 避免使用JavaScript修復佈局。

(6)使用高效的事件處理

  • 減小綁定事件監聽的節點,如經過事件委託;
  • 儘早處理事件,在DOMContentLoaded便可進行,不用等到load之後。

6、圖片

(1)優化圖片

嘗試把GIF格式轉換成PNG格式,看看是否節省空間。在全部的PNG圖片上運行pngcrush(或者其它PNG優化工具)。

YDN列出的相關工具缺少易用性,建議參考如下工具

TODO:

  • PNG 終極優化;
  • Webp 相關內容;
  • SVG 相關內容。

PNG終極優化

(2)優化CSS Sprite

  • 水平排列Sprite中的圖片,垂直排列會增長圖片大小;
  • Spirite中把顏色較近的組合在一塊兒能夠下降顏色數,理想情況是低於256色以適用PNG8格式;
  • 不要在Spirite的圖像中間留有較大空隙。減小空隙雖然不太影響文件大小,但能夠下降用戶代理把圖片解壓爲像素圖的內存消耗,對移動設備更友好。

(3)不要在HTML中縮放圖片

不要使用<img>的width、height縮放圖片,若是用到小圖片,就使用相應大小的圖片。若是須要

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

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

不少 CMS 和 CDN 都提供圖片裁切功能。

補充:設置圖片的寬和高,以避免瀏覽器按照「猜」的寬高給圖片保留的區域和實際寬高差別,產生重繪。

(4)使用體積小、可緩存的favicon.ico

Favicon.ico通常存放在網站根目錄下,不管是否在頁面中設置,瀏覽器都會嘗試請求這個文件。

因此確保這個圖標:

  • 存在(避免 404);
  • 儘可能小,最好小於 1K;
  • 設置較長的過時時間。

對於較新的瀏覽器,可使用PNG格式的favicon。

7、移動端

(1)保證全部組件都小於25K

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

保證全部組件都小於25K

(2)打包內容爲分段(multipart)文檔

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

總結

寫到這裏,雅虎的35條軍規算是介紹完了。條目雖然不少,但通過分類,能夠發現,性能優化主要切入點能夠從如下幾個方面去考慮:

  • 資源自己大小的壓縮優化(想辦法減小資源的體積)
  • 網絡請求的全過程(從url地址欄輸入發送請求開始到返回響應包的每一個環節)
  • 瀏覽器渲染的全過程(拿到資源後瀏覽器渲染的每一個環節)

所以,要完全掌握優化的方法,必須對http請求的全過程以及瀏覽器的渲染全過程都有深刻的理解。

前端性能優化

原文出自本人博客:

雅虎35條軍規——前端性能優化

相關文章
相關標籤/搜索