本文將詳細介紹前端性能優化的七大手段,包括減小請求數量、減少資源大小、優化網絡鏈接、優化資源加載、減小重繪迴流、使用性能更好的API和構建優化javascript
【合併】css
若是不進行文件合併,有以下3個隱患html
一、文件與文件之間有插入的上行請求,增長了N-1個網絡延遲前端
二、受丟包問題影響更嚴重java
三、通過代理服務器時可能會被斷開webpack
可是,文件合併自己也有本身的問題web
一、首屏渲染問題算法
二、緩存失效問題chrome
因此,對於文件合併,有以下改進建議瀏覽器
一、公共庫合併
二、不一樣頁面單獨合併
【圖片處理】
一、雪碧圖
CSS雪碧圖是之前很是流行的技術,把網站上的一些圖片整合到一張單獨的圖片中,能夠減小網站的HTTP請求數量,可是當整合圖片比較大時,一次加載比較慢。隨着字體圖片、SVG圖片的流行,該技術漸漸退出了歷史舞臺
二、Base64
將圖片的內容以Base64格式內嵌到HTML中,能夠減小HTTP請求數量。可是,因爲Base64編碼用8位字符表示信息中的6個位,因此編碼後大小大約比原始值擴大了 33%
三、使用字體圖標來代替圖片
【減小重定向】
儘可能避免使用重定向,當頁面發生了重定向,就會延遲整個HTML文檔的傳輸。在HTML文檔到達以前,頁面中不會呈現任何東西,也沒有任何組件會被下載,下降了用戶體驗
若是必定要使用重定向,如http重定向到https,要使用301永久重定向,而不是302臨時重定向。由於,若是使用302,則每一次訪問http,都會被重定向到https的頁面。而永久重定向,在第一次從http重定向到https以後 ,每次訪問http,會直接返回https的頁面
【使用緩存】
使用cach-control或expires這類強緩存時,緩存不過時的狀況下,不向服務器發送請求。強緩存過時時,會使用last-modified或etag這類協商緩存,向服務器發送請求,若是資源沒有變化,則服務器返回304響應,瀏覽器繼續從本地緩存加載資源;若是資源更新了,則服務器將更新後的資源發送到瀏覽器,並返回200響應
【不使用CSS @import】
CSS的@import會形成額外的請求
【避免使用空的src和href】
a標籤設置空的href,會重定向到當前的頁面地址
form設置空的method,會提交表單到當前的頁面地址
【壓縮】
一、HTML壓縮
HTML代碼壓縮就是壓縮在文本文件中有意義,可是在HTML中不顯示的字符,包括空格,製表符,換行符等
二、CSS壓縮
CSS壓縮包括無效代碼刪除與CSS語義合併
三、JS壓縮與混亂
JS壓縮與混亂包括無效字符及註釋的刪除、代碼語義的縮減和優化、下降代碼可讀性,實現代碼保護
四、圖片壓縮
針對真實圖片狀況,捨棄一些相對可有可無的色彩信息
【webp】
在安卓下可使用webp格式的圖片,它具備更優的圖像數據壓縮算法,能帶來更小的圖片體積,同等畫面質量下,體積比jpg、png少了25%以上,並且同時具有了無損和有損的壓縮模式、Alpha 透明以及動畫的特性
【開啓gzip】
HTTP協議上的GZIP編碼是一種用來改進WEB應用程序性能的技術。大流量的WEB站點經常使用GZIP壓縮技術來讓用戶感覺更快的速度。這通常是指WWW服務器中安裝的一個功能,當有人來訪問這個服務器中的網站時,服務器中的這個功能就將網頁內容壓縮後傳輸到來訪的電腦瀏覽器中顯示出來。通常對純文本內容可壓縮到原大小的40%
【使用CDN】
CDN全稱是Content Delivery Network,即內容分發網絡,它可以實時地根據網絡流量和各節點的鏈接、負載情況以及到用戶的距離和響應時間等綜合信息將用戶的請求從新導向離用戶最近的服務節點上。其目的是使用戶可就近取得所需內容,解決 Internet網絡擁擠的情況,提升用戶訪問網站的響應速度
【使用DNS預解析】
當瀏覽器訪問一個域名的時候,須要解析一次DNS,得到對應域名的ip地址。在解析過程當中,按照瀏覽器緩存
、系統緩存
、路由器緩存
、ISP(運營商)DNS緩存
、根域名服務器
、頂級域名服務器
、主域名服務器
的順序,逐步讀取緩存,直到拿到IP地址
DNS Prefetch,即DNS預解析就是根據瀏覽器定義的規則,提早解析以後可能會用到的域名,使解析結果緩存到系統緩存
中,縮短DNS解析時間,來提升網站的訪問速度
方法是在 head 標籤裏面寫上幾個 link 標籤
<link rel="dns-prefecth" href="https://www.google.com"> <link rel="dns-prefecth" href="https://www.google-analytics.com">
對以上幾個網站提早解析 DNS,因爲它是並行的,不會堵塞頁面渲染,這樣能夠縮短資源加載的時間
【並行鏈接】
因爲在HTTP1.1協議下,chrome每一個域名的最大併發數是6個。使用多個域名,能夠增長併發數
【持久鏈接】
使用keep-alive或presistent來創建持久鏈接,持久鏈接下降了時延和鏈接創建的開銷,將鏈接保持在已調諧狀態,並且減小了打開鏈接的潛在數量
【管道化鏈接】
在HTTP2協議中,能夠開啓管道化鏈接,即單條鏈接的多路複用,每條鏈接中併發傳輸多個資源,這裏就不須要添加域名來增長併發數了
【資源加載位置】
經過優化資源加載位置,更改資源加載時機,使盡量快地展現出頁面內容,儘量快地使功能可用
一、CSS文件放在head中,先外鏈,後本頁
二、JS文件放在body底部,先外鏈,後本頁
三、處理頁面、處理頁面佈局的JS文件放在head中,如babel-polyfill.js文件、flexible.js文件
四、body中間儘可能不寫style標籤和script標籤
【資源加載時機】
一、異步script標籤
defer: 異步加載,在HTML解析完成後執行。defer的實際效果與將代碼放在body底部相似
async: 異步加載,加載完成後當即執行
二、模塊按需加載
在SPA等業務邏輯比較複雜的系統中,須要根據路由來加載當前頁面須要的業務模塊
按需加載,是一種很好的優化網頁或應用的方式。這種方式其實是先把代碼在一些邏輯斷點處分離開,而後在一些代碼塊中完成某些操做後,當即引用或即將引用另一些新的代碼塊。這樣加快了應用的初始加載速度,減輕了它的整體體積,由於某些代碼塊可能永遠不會被加載
webpack 提供了兩個相似的技術,優先選擇的方式是使用符合 ECMAScript 提案 的 import() 語法。第二種則是使用 webpack 特定的 require.ensure
三、使用資源預加載preload和資源預讀取prefetch
preload讓瀏覽器提早加載指定資源,須要執行時再執行,能夠加速本頁面的加載速度
prefetch告訴瀏覽器加載下一頁面可能會用到的資源,能夠加速下一個頁面的加載速度
四、資源懶加載與資源預加載
資源延遲加載也稱爲懶加載,延遲加載資源或符合某些條件時才加載某些資源
資源預加載是提早加載用戶所需的資源,保證良好的用戶體驗
資源懶加載和資源預加載都是一種錯峯操做,在瀏覽器忙碌的時候不作操做,瀏覽器空間時,再加載資源,優化了網絡性能
【樣式設置】
一、避免使用層級較深的選擇器,或其餘一些複雜的選擇器,以提升CSS渲染效率
二、避免使用CSS表達式,CSS表達式是動態設置CSS屬性的強大但危險方法,它的問題就在於計算頻率很快。不只僅是在頁面顯示和縮放時,就是在頁面滾動、乃至移動鼠標時都會要從新計算一次
三、元素適當地定義高度或最小高度,不然元素的動態內容載入時,會出現頁面元素的晃動或位置,形成迴流
四、給圖片設置尺寸。若是圖片不設置尺寸,首次載入時,佔據空間會從0到徹底出現,上下左右均可能位移,發生迴流
五、不要使用table佈局,由於一個小改動可能會形成整個table從新佈局。並且table渲染一般要3倍於同等元素時間
六、可以使用CSS實現的效果,儘可能使用CSS而不使用JS實現
【渲染層】
一、此外,將須要屢次重繪的元素獨立爲render layer渲染層,如設置absolute,能夠減小重繪範圍
二、對於一些進行動畫的元素,使用硬件渲染,從而避免重繪和迴流
【DOM優化】
一、緩存DOM
const div = document.getElementById('div')
因爲查詢DOM比較耗時,在同一個節點無需屢次查詢的狀況下,能夠緩存DOM
二、減小DOM深度及DOM數量
HTML 中標籤元素越多,標籤的層級越深,瀏覽器解析DOM並繪製到瀏覽器中所花的時間就越長,因此應儘量保持 DOM 元素簡潔和層級較少。
三、批量操做DOM
因爲DOM操做比較耗時,且可能會形成迴流,所以要避免頻繁操做DOM,能夠批量操做DOM,先用字符串拼接完畢,再用innerHTML更新DOM
四、批量操做CSS樣式
經過切換class或者使用元素的style.csstext屬性去批量操做元素樣式
五、在內存中操做DOM
使用DocumentFragment對象,讓DOM操做發生在內存中,而不是頁面上
六、DOM元素離線更新
對DOM進行相關操做時,例、appendChild等均可以使用Document Fragment對象進行離線操做,帶元素「組裝」完成後再一次插入頁面,或者使用display:none 對元素隱藏,在元素「消失」後進行相關操做
七、DOM讀寫分離
瀏覽器具備惰性渲染機制,鏈接屢次修改DOM可能只觸發瀏覽器的一次渲染。而若是修改DOM後,當即讀取DOM。爲了保證讀取到正確的DOM值,會觸發瀏覽器的一次渲染。所以,修改DOM的操做要與訪問DOM分開進行
八、事件代理
事件代理是指將事件監聽器註冊在父級元素上,因爲子元素的事件會經過事件冒泡的方式向上傳播到父節點,所以,能夠由父節點的監聽函數統一處理多個子元素的事件
利用事件代理,能夠減小內存使用,提升性能及下降代碼複雜度
九、防抖和節流
使用函數節流(throttle)或函數去抖(debounce),限制某一個方法的頻繁觸發
十、及時清理環境
及時消除對象引用,清除定時器,清除事件監聽器,建立最小做用域變量,能夠及時回收內存
一、用對選擇器
選擇器的性能排序以下所示,儘可能選擇性能更好的選擇器
id選擇器(#myid) 類選擇器(.myclassname) 標籤選擇器(div,h1,p) 相鄰選擇器(h1+p) 子選擇器(ul > li) 後代選擇器(li a) 通配符選擇器(*) 屬性選擇器(a[rel="external"]) 僞類選擇器(a:hover,li:nth-child)
二、使用requestAnimationFrame來替代setTimeout和setInterval
但願在每一幀剛開始的時候對頁面進行更改,目前只有使用 requestAnimationFrame 可以保證這一點。使用 setTimeout 或者 setInterval 來觸發更新頁面的函數,該函數可能在一幀的中間或者結束的時間點上調用,進而致使該幀後面須要進行的事情沒有完成,引起丟幀
三、使用IntersectionObserver來實現圖片可視區域的懶加載
傳統的作法中,須要使用scroll事件,並調用getBoundingClientRect方法,來實現可視區域的判斷,即便使用了函數節流,也會形成頁面迴流。使用IntersectionObserver,則沒有上述問題
四、使用web worker
客戶端javascript一個基本的特性是單線程:好比,瀏覽器沒法同時運行兩個事件處理程序,它也沒法在一個事件處理程序運行的時候觸發一個計時器。Web Worker是HTML5提供的一個javascript多線程解決方案,能夠將一些大計算量的代碼交由web Worker運行,從而避免阻塞用戶界面,在執行復雜計算和數據處理時,這個API很是有用
可是,使用一些新的API的同時,也要注意其瀏覽器兼容性
【打包公共代碼】
使用CommonsChunkPlugin插件,將公共模塊拆出來,最終合成的文件可以在最開始的時候加載一次,便存到緩存中供後續使用。這會帶來速度上的提高,由於瀏覽器會迅速將公共的代碼從緩存中取出來,而不是每次訪問一個新頁面時,再去加載一個更大的文件
webpack 4 將移除 CommonsChunkPlugin, 取而代之的是兩個新的配置項 optimization.splitChunks 和 optimization.runtimeChunk
經過設置 optimization.splitChunks.chunks: "all" 來啓動默認的代碼分割配置項
【動態導入和按需加載】
webpack提供了兩種技術經過模塊的內聯函數調用來分離代碼,優先選擇的方式是,使用符合 ECMAScript 提案 的 import() 語法。第二種,則是使用 webpack 特定的 require.ensure
【剔除無用代碼】
tree shaking 是一個術語,一般用於描述移除 JavaScript 上下文中的未引用代碼(dead-code)。它依賴於 ES2015 模塊系統中的靜態結構特性,例如 import 和 export。這個術語和概念其實是興起於 ES2015 模塊打包工具 rollup
JS的tree shaking主要經過uglifyjs插件來完成,CSS的tree shaking主要經過purify CSS來實現的
【長緩存優化】
一、將hash替換爲chunkhash,這樣當chunk不變時,緩存依然有效
二、使用Name而不是id
每一個 module.id 會基於默認的解析順序(resolve order)進行增量。也就是說,當解析順序發生變化,ID 也會隨之改變
下面來使用兩個插件解決這個問題。第一個插件是 NamedModulesPlugin,將使用模塊的路徑,而不是數字標識符。雖然此插件有助於在開發過程當中輸出結果的可讀性,然而執行時間會長一些。第二個選擇是使用 HashedModuleIdsPlugin,推薦用於生產環境構建
【公用代碼內聯】
使用html-webpack-inline-chunk-plugin插件將mainfest.js內聯到html文件中