前端性能的一個重要指標是頁面加載時間,不只事關用戶體驗,也是搜索引擎排名考慮的一個因素。javascript
Web 前端 80% 的響應時間花在圖片、樣式、腳本等資源下載上。瀏覽器對每一個域名的鏈接數是有限制的,減小請求次數是縮短響應時間的關鍵。
經過簡潔的設計減小頁面所需資源,進而減小 HTTP 請求,這是最直接的方式,前提是你的 Boss、設計師同事不打死你。因此,仍是另闢蹊徑吧:
合併 JavaScript、CSS 等文件;服務器端(CDN)自動合併。
基於 Node.js 的文件合併工具一抓一大把
使用CSS Sprite:將背景圖片合併成一個文件,經過background-image
和 background-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
用戶輸入 URL 之後,瀏覽器首先要查詢域名(hostname)對應服務器的 IP 地址,通常須要耗費 20-120 毫秒 時間。DNS 查詢完成以前,瀏覽器沒法從服務器下載任何數據。
基於性能考慮,ISP、局域網、操做系統、瀏覽器都會有相應的 DNS 緩存機制。css
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 或者其餘服務器相似功能實現跳轉。前端
Ajax 能夠提升用戶體驗。但「異步」不意味着「及時」,優化 Ajax 響應速度提升性能還是須要關注的主題。最重要的的優化方式是緩存響應結果,詳見 添加 Expires 或 Cache-Control 響應頭。
如下規則也關乎 Ajax 響應速度:java
頁面初始加載時哪些內容是絕對必需的? 不在答案之列的資源均可以延遲加載。好比:非首屏使用的數據、樣式、腳本、圖片等;用戶交互時纔會顯示的內容。
遵循「漸進加強」理念開發的網站:JavaScript 用於加強用用戶體驗,但沒有(不支持) JavaScript 也能正常工做,徹底能夠延遲加載 JavaScript。
延遲渲染
將首屏之外的 HTML 放在不渲染的元素中,如隱藏的 <textarea>
,或者 type 屬性爲非執行腳本的 <script>
標籤中,減小初始渲染的 DOM 元素數量,提升速度。等首屏加載完成或者用戶操做時,再去渲染剩餘的頁面內容。git
預先加載利用瀏覽器空閒時間請求未來要使用的資源,以便用戶訪問下一頁面時更快地響應。github
複雜的頁面不只下載的字節更多,JavaScript DOM 操做也更慢。例如,同是添加一個事件處理器,500 個元素和 5000 個元素的頁面速度上會有很大區別。
從如下幾個角度考慮移除沒必要要的標記:ajax
document.getElementsByTagName('*').length;
對比標記良好的的網站,看看差距是多少。瀏覽器通常會限制每一個域的並行線程(通常爲 6 個,甚至更少),使用不一樣的域名能夠最大化下載線程,但注意保持在 2-4 個域名內,以免 DNS 查詢損耗。
例如,動態內容放在 csspod.com 上,靜態資源放在 static.csspod.com 上。這樣還能夠禁用靜態資源域下的 Cookie,減小數據傳輸,詳見 Cookie 優化。算法
使用 iframe 能夠在頁面中嵌入 HTML 文檔,但有利有弊。
<iframe>
優勢:
能夠用來加載速度較慢的第三方資源,如廣告、徽章;
可用做安全沙箱;
能夠並行下載腳本。
<iframe>
缺點:
加載代價昂貴,即便是空的頁面;
阻塞頁面 load 事件觸發;
Iframe 徹底加載之後,父頁面纔會觸發 load 事件。 Safari、Chrome 中經過 JavaScript 動態設置 iframe src 能夠避免這個問題。
缺少語義。
HTTP 請求很昂貴,返回無效的響應(如 404 未找到)徹底不必,下降用戶體驗並且毫無益處。
一些網站設計很酷炫、有提示信息的 404 頁面,有助於提升用戶體驗,但仍是浪費服務器資源。尤爲糟糕的是外部腳本返回 404,不只阻塞其餘資源下載,瀏覽器還會嘗試把 404 頁面內容看成 JavaScript 解析,消耗更多資源。
補充規則:
定義字符集,並放在 頂部。大多數瀏覽器會暫停頁面渲染,直到找到字符集定義。
用戶請求頁面時,服務器一般須要花費 200 ~ 500 毫秒來組合 HTML 頁面。在此期間,瀏覽器處於空閒、等待數據狀態。使用PHP 中的 flush() 函數,能夠發送部分已經準備好的 HTML 到瀏覽器,以便服務器還在忙於處理剩餘頁面時,瀏覽器能夠提早開始獲取資源。
能夠考慮在 以後輸出一次緩衝,HTML head 通常比較容易生成,先發送以便瀏覽器開始獲取 裏引用的 CSS 等資源。
Example:
<!-- css, js --> </head> <?php flush(); ?> <body> <!-- content -->
瀏覽器執行 XMLHttpRequest POST 請求時分紅兩步,先發送 Header,再發送數據。而 GET 只使用一個 TCP 數據包發送數據,因此首選 GET 方法。
根據 HTTP 規範,GET 用於獲取數據,POST 則用於向服務器發送數據,因此 Ajax 請求數據時使用 GET 更符合規範(GET 和 POST 對比)。
IE 中最大 URL 長度爲 2K,若是超出 2K,則須要考慮使用 POST 方法。
圖片 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 傳送的形成的流量浪費,提升響應速度。
把樣式表放在 中
把樣式表放在 中可讓頁面漸進渲染,儘早呈現視覺反饋,給用戶加載速度很快的感受。
這對內容比較多的頁面尤其重要,用戶能夠先查看已經下載渲染的內容,而不是盯着白屏等待。
若是把樣式表放在頁面底部,一些瀏覽器爲減小重繪,會在 CSS 加載完成之後才渲染頁面,用戶只能對着白屏乾瞪眼,用戶體驗極差。
不要使用 CSS 表達式
CSS 表達式能夠在 CSS 裏執行 JavaScript,僅 IE5-IE7 支持,IE8 標準模式已經廢棄。
CSS 表達式超出預期的頻繁執行,頁面滾動、鼠標移動時都會不斷執行,帶來很大的性能損耗。
IE7 及更低版本的瀏覽器已經逐漸成爲歷史,忘記它吧。
使用 <link>
替代 @import
對於 IE 某些版本,@import
的行爲和 <link>
放在頁面底部同樣。因此,不要用它。
不要使用 filter
AlphaImageLoader 爲 IE5.5-IE8 專有的技術,和 CSS 表達式同樣,放進博物館吧。
_注意:_這裏所說的不是 CSS3 Filter
把腳本放在頁面底部
瀏覽器下載腳本時,會阻塞其餘資源並行下載,即便是來自不一樣域名的資源。所以,最好將腳本放在底部,以提升頁面加載速度。
一些特殊場景沒法將腳本放到頁面底部的,能夠考慮 <script>
的如下屬性:
外部 JavaScript 和 CSS 文件能夠被瀏覽器緩存,在不一樣頁面間重用,也能下降頁面大小。
固然,實際中也須要考慮代碼的重用程度。若是僅僅是某個頁面使用到的代碼,能夠考慮內嵌在頁面中,減小 HTTP 請求數。另外,能夠在首頁加載完成之後,預先加載子頁面的資源。
壓縮代碼能夠移除非功能性的字符(註釋、空格、空行等),減小文件大小,提升載入速度。
得益於 Node.js 的流行,開源社區涌現出許多高效、易用的前端優化工具,JavaScript 和 CSS 壓縮類的,不敢說多如牛毛,多入雞毛卻是一點不誇張,如 [UglifyJS 2] (https://github.com/mishoo/UglifyJS2)、csso、cssnano 等。
對於內嵌的 CSS 和 JavaScript,也能夠經過 htmlmin 等工具壓縮。
這些項目都有 Gulp、Webpack 等流行構建工具的配套版本。
重複的腳本不只產生沒必要要的 HTTP 請求,並且重複解析執行浪費時間和計算資源。
JavaScript 操做 DOM 很慢,尤爲是 DOM 節點不少時。
使用時應該注意:
減小綁定事件監聽的節點,如經過事件委託;
儘早處理事件,在 DOMContentLoaded 便可進行,不用等到 load 之後。
對於 resize、scroll 等觸發頻率極高的事件,應該經過 debounce 等機制下降處理程序執行頻率。
TODO: 補充相關內容
優化圖片
YDN 列出的相關工具 缺少易用性,建議參考如下工具。
imagemin
ImageOptim
TODO:
PNG 終極優化;
Webp 相關內容;
SVG 相關內容。
PNG 終極優化:
水平排列 Sprite 中的圖片,垂直排列會增長圖片大小;
Spirite 中把顏色較近的組合在一塊兒能夠下降顏色數,理想情況是低於 256 色以適用 PNG8 格式;
不要在 Spirite 的圖像中間留有較大空隙。減小空隙雖然不太影響文件大小,但能夠下降用戶代理把圖片解壓爲像素圖的內存消耗,對移動設備更友好。
不要在 HTML 中縮放圖片
不要使用 的 width、height 縮放圖片,若是用到小圖片,就使用相應大小的圖片。
不少 CMS 和 CDN 都提供圖片裁切功能。
Favicon.ico 通常存放在網站根目錄下,不管是否在頁面中設置,瀏覽器都會嘗試請求這個文件。
因此確保這個圖標:
圖片相關補充
設置圖片的寬和高,以避免瀏覽器按照「猜」的寬高給圖片保留的區域和實際寬高差別,產生重繪。
移動端優化相關內容有待進一步整理補充。