前端性能優化

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

  • 來自 Google 的數據代表,一個有 10 條數據 0.4 秒能加載完的頁面,變成 30 條數據 0.9 秒加載完以後,流量和廣告收入降低 90%。
  • Google Map 首頁文件大小從 100KB 減少到 70-80KB 後,流量在第一週漲了 10%,接下來的三週漲了 25%。
  • 亞馬遜的數據代表:加載時間增長 100 毫秒,銷量就降低 1%。
    以上數據更說明「加載時間就是金錢」,前端優化主要圍繞提升加載速度進行。

1. 頁面內容

1.1 減小 HTTP 請求數

Web 前端 80% 的響應時間花在圖片、樣式、腳本等資源下載上。瀏覽器對每一個域名的鏈接數是有限制的,減小請求次數是縮短響應時間的關鍵。
經過簡潔的設計減小頁面所需資源,進而減小 HTTP 請求,這是最直接的方式,前提是你的 Boss、設計師同事不打死你。因此,仍是另闢蹊徑吧:
合併 JavaScript、CSS 等文件;服務器端(CDN)自動合併。
基於 Node.js 的文件合併工具一抓一大把
使用CSS Sprite:將背景圖片合併成一個文件,經過background-imagebackground-position 控制顯示;
Sprite Cow
Spritebox
逐步被 Icon Font 和 SVG Sprite 取代。
Image Map:合併圖片,而後使用座標映射不一樣的區域。
缺點:僅適用於相連的圖片;設置座標過程乏味且易出錯;可訪性問題。不推薦使用這種過期的技術。
Inline Assets:使用 Data URI scheme 將圖片嵌入 HTML 或者 CSS 中;或者將 CSS、JS、圖片直接嵌入 HTML 中。
會增長文件大小,也可能產生瀏覽器兼容及其餘性能問題(有待整理補充)。
將來的趨勢是使用內嵌 SVG。
內容分片,將請求劃分到不一樣的域名上。
HTTP/2 經過多路複用大幅下降了多個請求的開銷。經過數據分幀層,客戶端和服務器之間只須要創建一個 TCP 鏈接,便可同時收發多個文件,並且,該鏈接在至關長的時間週期內保持打開(持久化),以便複用。
HTTP/2 的新特性意味着上述優化實踐再也不適用,但考慮到客戶端對 HTTP/2 的支持覆蓋程度,還需根據實際數據權衡。php

1.2 減小 DNS 查詢

用戶輸入 URL 之後,瀏覽器首先要查詢域名(hostname)對應服務器的 IP 地址,通常須要耗費 20-120 毫秒 時間。DNS 查詢完成以前,瀏覽器沒法從服務器下載任何數據。
基於性能考慮,ISP、局域網、操做系統、瀏覽器都會有相應的 DNS 緩存機制。css

  • IE 緩存 30 分鐘,能夠經過註冊表中 DnsCacheTimeout 項設置;
  • Firefox 混存 1 分鐘,經過 network.dnsCacheExpiration 配置;
  • (TODO:補充其餘瀏覽器緩存信息)
    首次訪問、沒有相應的 DNS 緩存時,域名越多,查詢時間越長。因此應儘可能減小域名數量。但基於並行下載考慮,把資源分佈到 2 個域名上(最多不超過 4 個)。這是減小 DNS 查詢同時保證並行下載的折衷方案。

1.3 避免重定向

HTTP 重定向經過 301/302 狀態碼實現。html

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

客戶端收到服務器的重定向響應後,會根據響應頭中 Location 的地址再次發送請求。重定向會影響用戶體驗,尤爲是屢次重定向時,用戶在一段時間內看不到任何內容,只看到瀏覽器進度條一直在刷新。
有時重定向沒法避免,在糟糕也比拋出 404 好。雖然經過 HTML meta refresh 和 JavaScript 也能實現,但首選 HTTP 3xx 跳轉,以保證瀏覽器「後退」功能正常工做(也利於 SEO)。
最浪費的重定向常常發生、並且很容易被忽略:URL 末尾應該添加 / 但未添加。好比,訪問 http://astrology.yahoo.com/astrology 將被 301 重定向到 http://astrology.yahoo.com/astrology/(注意末尾的 /)。若是使用 Apache,能夠經過 Alias 或 mod_rewrite 或 DirectorySlash 解決這個問題。
網站域名變動:CNAME 結合 Alias 或 mod_rewrite 或者其餘服務器相似功能實現跳轉。前端

1.4 緩存 Ajax 請求

Ajax 能夠提升用戶體驗。但「異步」不意味着「及時」,優化 Ajax 響應速度提升性能還是須要關注的主題。最重要的的優化方式是緩存響應結果,詳見 添加 Expires 或 Cache-Control 響應頭。
如下規則也關乎 Ajax 響應速度:java

  • 啓用 Gzip
  • 減小 DNS 查詢
  • 壓縮 JavaScript 和 CSS
  • 避免重定向
  • 配置 Etag

1.5 延遲加載

頁面初始加載時哪些內容是絕對必需的? 不在答案之列的資源均可以延遲加載。好比:非首屏使用的數據、樣式、腳本、圖片等;用戶交互時纔會顯示的內容。
遵循「漸進加強」理念開發的網站:JavaScript 用於加強用用戶體驗,但沒有(不支持) JavaScript 也能正常工做,徹底能夠延遲加載 JavaScript。
延遲渲染
將首屏之外的 HTML 放在不渲染的元素中,如隱藏的 <textarea>,或者 type 屬性爲非執行腳本的 <script> 標籤中,減小初始渲染的 DOM 元素數量,提升速度。等首屏加載完成或者用戶操做時,再去渲染剩餘的頁面內容。git

1.6 預先加載

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

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

1.7 減小 DOM 元素數量

複雜的頁面不只下載的字節更多,JavaScript DOM 操做也更慢。例如,同是添加一個事件處理器,500 個元素和 5000 個元素的頁面速度上會有很大區別。
從如下幾個角度考慮移除沒必要要的標記:ajax

  • 是否還在使用表格佈局?
  • 塞進去更多的
    僅爲了處理佈局問題?也許有更好、更語義化的標記。
  • 能經過僞元素實現的功能,就不必添加額外元素,如清除浮動。
    瀏覽器控制檯中輸入如下代碼能夠計算出頁面中有多少 DOM 元素:document.getElementsByTagName('*').length; 對比標記良好的的網站,看看差距是多少。
    爲何不使用表格佈局?
    更多的標籤,增長文件大小;不易維護,沒法適應響應式設計;性能考量,默認的表格佈局算法會產生大量重繪(參見表格佈局算法)。

1.8 劃份內容到不一樣域名

瀏覽器通常會限制每一個域的並行線程(通常爲 6 個,甚至更少),使用不一樣的域名能夠最大化下載線程,但注意保持在 2-4 個域名內,以免 DNS 查詢損耗。
例如,動態內容放在 csspod.com 上,靜態資源放在 static.csspod.com 上。這樣還能夠禁用靜態資源域下的 Cookie,減小數據傳輸,詳見 Cookie 優化。算法

1.9 儘可能減小 iframe 使用

使用 iframe 能夠在頁面中嵌入 HTML 文檔,但有利有弊。
<iframe> 優勢:
能夠用來加載速度較慢的第三方資源,如廣告、徽章;
可用做安全沙箱;
能夠並行下載腳本。
<iframe> 缺點:
加載代價昂貴,即便是空的頁面;
阻塞頁面 load 事件觸發;
Iframe 徹底加載之後,父頁面纔會觸發 load 事件。 Safari、Chrome 中經過 JavaScript 動態設置 iframe src 能夠避免這個問題。
缺少語義。

1.10 避免 404 錯誤

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

2. 服務器

2.1 儘早輸出(flush)緩衝

用戶請求頁面時,服務器一般須要花費 200 ~ 500 毫秒來組合 HTML 頁面。在此期間,瀏覽器處於空閒、等待數據狀態。使用PHP 中的 flush() 函數,能夠發送部分已經準備好的 HTML 到瀏覽器,以便服務器還在忙於處理剩餘頁面時,瀏覽器能夠提早開始獲取資源。
能夠考慮在 以後輸出一次緩衝,HTML head 通常比較容易生成,先發送以便瀏覽器開始獲取 裏引用的 CSS 等資源。
Example:

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

2.2 Ajax 請求使用 GET 方法

瀏覽器執行 XMLHttpRequest POST 請求時分紅兩步,先發送 Header,再發送數據。而 GET 只使用一個 TCP 數據包發送數據,因此首選 GET 方法。
根據 HTTP 規範,GET 用於獲取數據,POST 則用於向服務器發送數據,因此 Ajax 請求數據時使用 GET 更符合規範(GET 和 POST 對比)。
IE 中最大 URL 長度爲 2K,若是超出 2K,則須要考慮使用 POST 方法。

2.3 避免圖片 src 爲空

圖片 src 屬性值爲空字符串可能如下面兩種形式出現:
HTML:
<img src="" />
JavaScript:

var img = new Image();  
img.src = "";

雖然 src 屬性爲空字符串,但瀏覽器仍然會向服務器發起一個 HTTP 請求:
IE 向頁面所在的目錄發送請求;
Safari、Chrome、Firefox 向頁面自己發送請求;
Opera 不執行任何操做。
以上數據較老,當下主流版本可能會有改變。
空 src 產生請求的後果不容小覷:
給服務器形成意外的流量負擔,尤爲時日 PV 較大時;
浪費服務器計算資源;
可能產生報錯。
固然,瀏覽器如此實現也是根據 RFC 3986 - Uniform Resource Identifiers,當空字符串做爲 URI 出現時,被當成相對 URI,具體算法參見規範 5.2 節。
空的 href 屬性也存在相似問題。用戶點擊空連接時,瀏覽器也會向服務器發送 HTTP 請求,能夠經過 JavaScript 阻止空連接的默認的行爲。

Cookie 被用於身份認證、個性化設置等諸多用途。Cookie 經過 HTTP 頭在服務器和瀏覽器間來回傳送,減小 Cookie 大小能夠下降其對響應速度的影響。
去除沒必要要的 Cookie;
儘可能壓縮 Cookie 大小;
注意設置 Cookie 的 domain 級別,如無必要,不要影響到 sub-domain;
設置合適的過時時間。
HTTP/2 首部壓縮在客戶端和服務器端使用「首部表」來跟蹤和存儲以前發送的鍵值對,對於相同的數據,再也不隨每次請求和響應發送。

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

2.5 CSS

把樣式表放在 中
把樣式表放在 中可讓頁面漸進渲染,儘早呈現視覺反饋,給用戶加載速度很快的感受。
這對內容比較多的頁面尤其重要,用戶能夠先查看已經下載渲染的內容,而不是盯着白屏等待。
若是把樣式表放在頁面底部,一些瀏覽器爲減小重繪,會在 CSS 加載完成之後才渲染頁面,用戶只能對着白屏乾瞪眼,用戶體驗極差。
不要使用 CSS 表達式
CSS 表達式能夠在 CSS 裏執行 JavaScript,僅 IE5-IE7 支持,IE8 標準模式已經廢棄。
CSS 表達式超出預期的頻繁執行,頁面滾動、鼠標移動時都會不斷執行,帶來很大的性能損耗。
IE7 及更低版本的瀏覽器已經逐漸成爲歷史,忘記它吧。
使用 <link> 替代 @import
對於 IE 某些版本,@import 的行爲和 <link> 放在頁面底部同樣。因此,不要用它。
不要使用 filter
AlphaImageLoader 爲 IE5.5-IE8 專有的技術,和 CSS 表達式同樣,放進博物館吧。
_注意:_這裏所說的不是 CSS3 Filter

2.6 JavaScript

把腳本放在頁面底部
瀏覽器下載腳本時,會阻塞其餘資源並行下載,即便是來自不一樣域名的資源。所以,最好將腳本放在底部,以提升頁面加載速度。
一些特殊場景沒法將腳本放到頁面底部的,能夠考慮 <script> 的如下屬性:

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

2.7 使用外部 JavaScript 和 CSS

外部 JavaScript 和 CSS 文件能夠被瀏覽器緩存,在不一樣頁面間重用,也能下降頁面大小。
固然,實際中也須要考慮代碼的重用程度。若是僅僅是某個頁面使用到的代碼,能夠考慮內嵌在頁面中,減小 HTTP 請求數。另外,能夠在首頁加載完成之後,預先加載子頁面的資源。

2.8 壓縮 JavaScript 和 CSS

壓縮代碼能夠移除非功能性的字符(註釋、空格、空行等),減小文件大小,提升載入速度。
得益於 Node.js 的流行,開源社區涌現出許多高效、易用的前端優化工具,JavaScript 和 CSS 壓縮類的,不敢說多如牛毛,多入雞毛卻是一點不誇張,如 [UglifyJS 2] (https://github.com/mishoo/UglifyJS2)、csso、cssnano 等。
對於內嵌的 CSS 和 JavaScript,也能夠經過 htmlmin 等工具壓縮。
這些項目都有 Gulp、Webpack 等流行構建工具的配套版本。

2.9 移除重複腳本

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

2.10 減小 DOM 操做

JavaScript 操做 DOM 很慢,尤爲是 DOM 節點不少時。
使用時應該注意:

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

2.11 使用高效的事件處理

減小綁定事件監聽的節點,如經過事件委託;
儘早處理事件,在 DOMContentLoaded 便可進行,不用等到 load 之後。
對於 resize、scroll 等觸發頻率極高的事件,應該經過 debounce 等機制下降處理程序執行頻率。
TODO: 補充相關內容

2.12 圖片

優化圖片
YDN 列出的相關工具 缺少易用性,建議參考如下工具。
imagemin
ImageOptim
TODO:
PNG 終極優化;
Webp 相關內容;
SVG 相關內容。
PNG 終極優化:

2.13 優化 CSS Sprite

水平排列 Sprite 中的圖片,垂直排列會增長圖片大小;
Spirite 中把顏色較近的組合在一塊兒能夠下降顏色數,理想情況是低於 256 色以適用 PNG8 格式;
不要在 Spirite 的圖像中間留有較大空隙。減小空隙雖然不太影響文件大小,但能夠下降用戶代理把圖片解壓爲像素圖的內存消耗,對移動設備更友好。
不要在 HTML 中縮放圖片
不要使用 的 width、height 縮放圖片,若是用到小圖片,就使用相應大小的圖片。
不少 CMS 和 CDN 都提供圖片裁切功能。

2.14 使用體積小、可緩存的 favicon.ico

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

  • 存在(避免 404);
  • 儘可能小,最好小於 1K;
  • 設置較長的過時時間。
    對於較新的瀏覽器,可使用 PNG 格式的 favicon。

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

2.15 移動端

移動端優化相關內容有待進一步整理補充。

相關文章
相關標籤/搜索