前端效能優化的常用手段
在這個使用者體驗越來越重要的時代,你的頁面稍微有點卡頓,都難以挽留使用者。而作為一名有追求的前端,勢必要力所能及地優化我們前端頁面的效能。今天,就來談一談那些前端效能優化的一些常用手段。前端效能的一個重要指標是頁面載入時間,不僅事關使用者體驗,也是搜尋引擎排名考慮的一個因素。
以上資料更加說明「載入時間就是金錢」,前端優化主要圍繞提高載入速度進行。
|
目錄
【1】減少 HTTP 請求數 【1】使用 CDN 【1】減少 Cookie 大小
【2】減少 DNS 查詢 【2】新增 Expires 或 Cache-Control 響應頭 【2】靜態資源使用無 Cookie 域名
【5】延遲載入 | 延遲渲染 【5】儘早輸出(flush)緩衝
【1】把樣式表放在 head 中 【1】把指令碼放在頁面底部 【1】優化圖片
【2】不要使用 CSS 表示式 【2】使用外部 JavaScript 和 CSS 【2】優化 CSS Sprite
【3】使用 link標籤替代 @import 【3】壓縮 JavaScript 和 CSS 【3】不要在 HTML 中縮放圖片
【4】不要使用 filter 【4】移除重複指令碼 【4】使用體積小、可快取的 favicon.ico
頁面內容
【1】減少 HTTP 請求數
Web 前端 80% 的響應時間花在圖片、樣式、指令碼等資源下載上。瀏覽器對每個域名的連線數是有限制的,減少請求次數是縮短響應時間的關鍵。
- 合併 JavaScript、CSS 等檔案,將瀏覽器一次訪問需要的javascript和CSS合併成一個檔案,這樣瀏覽器就只需要一次請求。
- 使用CSS Sprite:將背景圖片合併成一個檔案,通過
background-image
和background-position
控制顯示。逐步被 Icon Font 和 SVG Sprite 取代。 - 內容分片,將請求劃分到不同的域名上。
- LazyLoad Images (這條策略實際上並不一定能減少 HTTP請求數,但是卻能在某些條件下或者頁面剛載入時減少 HTTP請求數)
- 使用瀏覽器快取,將CSS、javascript、logo、圖示這些更新頻率較低的靜態資原始檔快取在瀏覽器中。
【2】減少 DNS 查詢
使用者輸入 URL 以後,瀏覽器首先要查詢域名(hostname)對應伺服器的 IP 地址,一般需要耗費 20-120 毫秒 時間。DNS 查詢完成之前,瀏覽器無法從伺服器下載任何資料。
基於效能考慮,ISP、區域網、作業系統、瀏覽器都會有相應的 DNS 快取機制。
- IE 快取 30 分鐘,可以通過登錄檔中
DnsCacheTimeout
項設定; - Firefox 混存 1 分鐘,通過
network.dnsCacheExpiration
配置; - (TODO:補充其他瀏覽器快取資訊)
首次訪問、沒有相應的 DNS 快取時,域名越多,查詢時間越長。所以應儘量減少域名數量。但基於並行下載考慮,把資源分佈到 2 個域名上(最多不超過 4 個)。這是減少 DNS 查詢同時保證並行下載的折衷方案。
【3】避免重定向
HTTP 重定向通過 301
/302
狀態碼實現。
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
或者其他伺服器類似功能實現跳轉。
【4】快取 Ajax 請求
Ajax 可以提高使用者體驗。但「非同步」不意味著「及時」,優化 Ajax 響應速度提高效能仍是需要關注的主題。
最重要的的優化方式是快取響應結果,詳見 新增 Expires 或 Cache-Control 響應頭。
以下規則也關乎 Ajax 響應速度:
【5】延遲載入 | 延遲渲染
頁面初始載入時哪些內容是絕對必需的?不在答案之列的資源都可以延遲載入。比如:
- 非首屏使用的資料、樣式、指令碼、圖片等;
- 使用者互動時才會顯示的內容。
遵循「漸進增強」理念開發的網站:JavaScript 用於增強用使用者體驗,但沒有(不支援) JavaScript 也能正常工作,完全可以延遲載入 JavaScript。
將首屏以外的 HTML 放在不渲染的元素中,如隱藏的 <textarea>
,或者 type
屬性為非執行指令碼的 <script>
標籤中,減少初始渲染的 DOM 元素數量,提高速度。等首屏載入完成或者使用者操作時,再去渲染剩餘的頁面內容。
【6】預先載入
預先載入利用瀏覽器空閒時間請求將來要使用的資源,以便使用者訪問下一頁面時更快地響應。
無條件預先載入:頁面載入完成(load
)後,馬上獲取其他資源。以 google.com 為例,首頁載入完成後會立即下載一個 Sprite 圖片,此圖首頁不需要,但是搜尋結果頁要用到。
有條件預先載入:根據使用者行為預判使用者去向,預載相關資源。比如 search.yahoo.com 開始輸入時會有額外的資源載入。Chrome 等瀏覽器的位址列也有類似的機制。
有「陰謀」的預先載入:頁面即將上線新版前預先載入新版內容。網站改版後由於快取、使用習慣等原因,會有舊版的網站更快更流暢的反饋。為緩解這一問題,在新版上線之前,舊版可以利用空閒提前載入一些新版的資源快取到客戶端,以便新版正式上線後更快的載入(好一個「心機猿」:scream:)。「雙十一」、「黑五」這類促銷日來臨之前,也可以預先下載一些相關資源到客戶端(瀏覽器、App 等),有效利用瀏覽器快取和本地儲存,降低活動當日請求壓力,提高使用者體驗。
TODO: Prefetch 相關細節: Resource Hints Spec
【7】減少 DOM 元素數量
複雜的頁面不僅下載的位元組更多,JavaScript DOM 操作也更慢。例如,同是新增一個事件處理器,500 個元素和 5000 個元素的頁面速度上會有很大區別。
從以下幾個角度考慮移除不必要的標記:
- 是否還在使用表格佈局?
- 塞進去更多的
<div>
僅為了處理佈局問題?也許有更好、更語義化的標記。 - 能通過偽元素實現的功能,就沒必要新增額外元素,如清除浮動。
瀏覽器控制檯中輸入以下程式碼可以計算出頁面中有多少 DOM 元素:
document.getElementsByTagName('*').length;
Q:為什麼不使用表格佈局?
- 更多的標籤,增加檔案大小;
- 不易維護,無法適應響應式設計;
- 效能考量,預設的表格佈局演算法會產生大量重繪(參見表格佈局演算法)。
【8】劃分內容到不同域名
瀏覽器一般會限制每個域的並行執行緒(一般為 6 個,甚至更少),使用不同的域名可以最大化下載執行緒,但注意保持在 2-4 個域名內,以避免 DNS 查詢損耗。
例如,動態內容放在 csspod.com
上,靜態資源放在 static.csspod.com
上。這樣還可以禁用靜態資源域下的 Cookie,減少資料傳輸,詳見 Cookie 優化。
更多資訊參考 Maximizing Parallel Downloads in the Carpool Lane
【9】儘量減少 iframe 使用
使用 iframe 可以在頁面中嵌入 HTML 文件,但有利有弊。
<iframe>
優點:
- 可以用來載入速度較慢的第三方資源,如廣告、徽章;
- 可用作安全沙箱;
- 可以並行下載指令碼。
<iframe> 缺點
:
- 載入代價昂貴,即使是空的頁面;
- 阻塞頁面
load
事件觸發。Iframe 完全載入以後,父頁面才會觸發load
事件。 Safari、Chrome 中通過 JavaScript 動態設定 iframesrc
可以避免這個問題。 - 缺乏語義。
【10】避免 404 錯誤
HTTP 請求很昂貴,返回無效的響應(如 404 未找到)完全沒必要,降低使用者體驗而且毫無益處。
一些網站設計很酷炫、有提示資訊的 404 頁面,有助於提高使用者體驗,但還是浪費伺服器資源。尤其糟糕的是外部指令碼返回 404,不僅阻塞其他資源下載,瀏覽器還會嘗試把 404 頁面內容當作 JavaScript 解析,消耗更多資源。
【11】補充規則
定義字符集,並放在
<head>
頂部。大多數瀏覽器會暫停頁面渲染,直到找到字符集定義。
伺服器
伺服器相關優化設定可參考 H5BP 相關專案:
- Nginx HTTP server boilerplate configs
- Apache HTTP server boilerplate configs
- IIS Web.Config Boilerplates
【1】使用 CDN
網站 80-90% 響應時間消耗在資源下載上,減少資源下載時間是效能優化的黃金髮則。
相比分散式架構的複雜和巨大投入,靜態內容分發網路(CDN)可以以較低的投入,獲得載入速度有效提升。
【2】新增 Expires 或 Cache-Control 響應頭
靜態內容:將
Expires
響應頭設定為將來很遠的時間,實現「永不過期」策略;動態內容:設定合適的
Cache-Control
響應頭,讓瀏覽器有條件地發起請求。鑑於靜態內容和動態內容不同的快取策略,實踐中一般會把二者部署在不同的伺服器(域名)以方便管理。
Cache-Control 頭在 HTTP/1.1 規範中定義,取代了之前用來定義響應快取策略的頭(例如 Expires、Pragma)。當前的所有瀏覽器都支援 Cache-Control,因此,使用它就夠了。
參考連結:
【3】啟用 Gzip
Gzip 壓縮通常可以減少 70% 的響應大小,對某些檔案更可能高達 90%,比 Deflate 更高效。主流 Web 伺服器都有相應模組,而且絕大多數瀏覽器支援 gzip 解碼。所以,應該對 HTML、CSS、JS、XML、JSON 等文字型別的內容啟用壓縮。
注意,圖片和 PDF 檔案不要使用 gzip。它們本身已經壓縮過,再使用 gzip 壓縮不僅浪費 CPU 資源,而且還可能增加檔案體積。
對於不支援的 Gzip 的使用者代理,通過設定 Vary 響應頭,返回未壓縮的資料:
Vary: *
【4】配置 Etag
Etag 通過檔案版本標識,方便伺服器判斷請求的內容是否有更新,如果沒有就響應 304
,避免重新下載。
當然,啟用 Etag 可能會導致其他問題,還需要根據具體情況做判斷。(TODO:補充相關內容)
【5】儘早輸出(flush)緩衝
使用者請求頁面時,伺服器通常需要花費 200 ~ 500 毫秒來組合 HTML 頁面。在此期間,瀏覽器處於空閒、等待資料狀態。使用PHP 中的 flush() 函式,可以傳送部分已經準備好的 HTML 到瀏覽器,以便伺服器還在忙於處理剩餘頁面時,瀏覽器可以提前開始獲取資源。
可以考慮在 </head>
之後輸出一次緩衝,HTML head 一般比較容易生成,先發送以便瀏覽器開始獲取 <head>
裡引用的 CSS 等資源。
<!-- css, js -->
</head>
<?php flush(); ?>
<body>
<!-- content -->
【6】Ajax 請求使用 GET 方法
瀏覽器執行 XMLHttpRequest POST 請求時分成兩步,先發送 Header,再發送資料。而 GET 只使用一個 TCP 資料包傳送資料,所以首選 GET 方法。
根據 HTTP 規範,GET 用於獲取資料,POST 則用於向伺服器傳送資料,所以 Ajax 請求資料時使用 GET 更符合規範(GET 和 POST 對比)。
IE 中最大 URL 長度為 2K,如果超出 2K,則需要考慮使用 POST 方法。
【7】避免圖片 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
【1】減少 Cookie 大小
Cookie 被用於身份認證、個性化設定等諸多用途。Cookie 通過 HTTP 頭在伺服器和瀏覽器間來回傳送,減少 Cookie 大小可以降低其對響應速度的影響。
- 去除不必要的 Cookie;
- 儘量壓縮 Cookie 大小;
- 注意設定 Cookie 的 domain 級別,如無必要,不要影響到 sub-domain;
- 設定合適的過期時間。
更多細節參考 When the Cookie Crumbles。
HTTP/2 首部壓縮在客戶端和伺服器端使用「首部表」來跟蹤和儲存之前傳送的鍵值對,對於相同的資料,不再隨每次請求和響應傳送。
【2】靜態資源使用無 Cookie 域名
靜態資源一般無需使用 Cookie,可以把它們放在使用二級域名或者專門域名的無 Cookie 伺服器上,降低 Cookie 傳送的造成的流量浪費,提高響應速度。
CSS
【1】把樣式表放在 head
中
把樣式表放在 <head>
中可以讓頁面漸進渲染,儘早呈現視覺反饋,給使用者載入速度很快的感覺。
這對內容比較多的頁面尤為重要,使用者可以先檢視已經下載渲染的內容,而不是盯著白屏等待。
如果把樣式表放在頁面底部,一些瀏覽器為減少重繪,會在 CSS 載入完成以後才渲染頁面,使用者只能對著白屏乾瞪眼,使用者體驗極差。
【2】不要使用 CSS 表示式
CSS 表示式可以在 CSS 裡執行 JavaScript,僅 IE5-IE7 支援,IE8 標準模式已經廢棄。
CSS 表示式超出預期的頻繁執行,頁面滾動、滑鼠移動時都會不斷執行,帶來很大的效能損耗。
IE7 及更低版本的瀏覽器已經逐漸成為歷史,忘記它吧。
【3】使用 link標籤替代 @import
對於 IE 某些版本,@import
的行為和 <link>
放在頁面底部一樣。所以,不要用它。
【4】不要使用 filter
AlphaImageLoader
為 IE5.5-IE8 專有的技術,和 CSS 表示式一樣,放進博物館吧。
注意:這裡所說的不是 CSS3 Filter,參考文章 Understanding CSS Filter Effects
JavaScript
【1】把指令碼放在頁面底部
瀏覽器下載指令碼時,會阻塞其他資源並行下載,即使是來自不同域名的資源。因此,最好將指令碼放在底部,以提高頁面載入速度。
一些特殊場景無法將指令碼放到頁面底部的,可以考慮 <script>
的以下屬性:
【2】使用外部 JavaScript 和 CSS
外部 JavaScript 和 CSS 檔案可以被瀏覽器快取,在不同頁面間重用,也能降低頁面大小。
當然,實際中也需要考慮程式碼的重用程度。如果僅僅是某個頁面使用到的程式碼,可以考慮內嵌在頁面中,減少 HTTP 請求數。另外,可以在首頁載入完成以後,預先載入子頁面的資源。
【3】壓縮 JavaScript 和 CSS
壓縮程式碼可以移除非功能性的字元(註釋、空格、空行等),減少檔案大小,提高載入速度。
得益於 Node.js 的流行,開源社群湧現出許多高效、易用的前端優化工具,JavaScript 和 CSS 壓縮類的,不敢說多如牛毛,多入雞毛倒是一點不誇張,如 [UglifyJS 2] (https://github.com/mishoo/UglifyJS2)、csso、cssnano 等。
對於內嵌的 CSS 和 JavaScript,也可以通過 htmlmin 等工具壓縮。
這些專案都有 Gulp、Webpack 等流行構建工具的配套版本。
【4】移除重複指令碼
重複的指令碼不僅產生不必要的 HTTP 請求,而且重複解析執行浪費時間和計算資源。
【5】減少 DOM 操作
JavaScript 操作 DOM 很慢,尤其是 DOM 節點很多時。
使用時應該注意:
- 快取已經訪問過的元素;
- 使用 DocumentFragment 暫存 DOM,整理好以後再插入 DOM 樹;
- 操作 className,而不是多次讀寫
style
; - 避免使用 JavaScript 修復佈局。
【6】使用高效的事件處理
- 減少繫結事件監聽的節點,如通過事件委託;
- 儘早處理事件,在
DOMContentLoaded
即可進行,不用等到load
以後。
對於 resize
、scroll
等觸發頻率極高的事件,應該通過 debounce 等機制降低處理程式執行頻率。
TODO: 補充相關內容 http://demo.nimius.net/debounce_throttle/
圖片
【1】優化圖片
YDN 列出的相關工具 缺乏易用性,建議參考以下工具。
PNG 終極優化:
【2】優化 CSS Sprite
- 水平排列 Sprite 中的圖片,垂直排列會增加圖片大小;
- Spirite 中把顏色較近的組合在一起可以降低顏色數,理想狀況是低於 256 色以適用 PNG8 格式;
- 不要在 Spirite 的影象中間留有較大空隙。減少空隙雖然不太影響檔案大小,但可以降低使用者代理把圖片解壓為畫素圖的記憶體消耗,對移動裝置更友好。
【3】不要在 HTML 中縮放圖片
不要使用 <img>
的 width
、height
縮放圖片,如果用到小圖片,就使用相應大小的圖片。
很多 CMS 和 CDN 都提供圖片裁切功能。
【4】使用體積小、可快取的 favicon.ico
Favicon.ico 一般存放在網站根目錄下,無論是否在頁面中設定,瀏覽器都會嘗試請求這個檔案。
所以確保這個圖示:
- 存在(避免 404);
- 儘量小,最好小於 1K;
- 設定較長的過期時間。
對於較新的瀏覽器,可以使用 PNG 格式的 favicon。
參考連結:Favicons, Touch Icons, Tile Icons, etc. Which Do You Need?
【5】圖片相關補充
設定圖片的寬和高,以免瀏覽器按照「猜」的寬高給圖片保留的區域和實際寬高差異,產生重繪。
參考連結
http://www.websiteoptimization.com/speed/tweak/psychology-web-performance/