前端的性能優化是一個龐大繁雜的領域,涉及到許許多多方面,包括網絡傳輸層面、客戶端加載渲染,乃至服務器端的優化等等。這些大的方向下,又有很是多小的分支。因此,前端的性能優化能夠說是許多雜項的集合,並且目前來講,並無很是成體系的這樣一套東西。
本文基於掘金小冊《前端性能優化原理與實踐》,是本身的學習總結,並在此基礎上作了一些我的的拓展和提煉,用於造成本身的前端性能優化的相關知識地圖。
前端
webpack的優化主要有兩個方向:
1.webpack的構建時間太長
2.webpack打包後的體積過大
針對這兩個問題,可使用相應的配置,或者插件來進行優化,總結以下:
a.使用exclude配置選項,排除非必要的編譯文件。
b.配置babel-loader的loader:'babel-loader?cacheDirectory=true',將編譯結果緩存至文件系統。
c.使用DllPlugin插件打包第三方庫,這個插件會將第三方庫打包到單獨的文件中,這個文件就是一個單獨的依賴庫,這個依賴庫不會跟隨業務代碼一塊兒從新打包,只有當依賴自身發生變化的時候纔會從新打包,從而加快了打包的速度。
d.使用Happypack插件將loader由單進程轉爲多進程。
e.使用webpack-bundle-analyzer查看打包後chunk的組成以及依賴,進行相應的優化。
f.使用webpack自帶的Tree-shaking功能進行模塊級別的冗餘代碼的刪除優化。
g.使用UglifyJsPlugin插件更細粒度的刪除冗餘代碼,包括console、註釋等。
h.使用webpack的按需加載功能。
webpack
在請求頭(Request Headers)中添加accept-encoding:gzip;能夠開啓Gzip壓縮。
Gzip壓縮一般可以減少70%左右的體積。它的原理是在一個文件中找出那些重複出現的字符串,臨時替換它們,從而使整個文件變小。
web
當下web應用比較普遍的圖片格式有:JPEG/JPG,PNG,WebP,Base64,SVG等。
在計算機中,像素用二進制位數來表示。一個像素對應的二進制位數越多,它能夠表示的顏色種類就越多。一個二進制位表示兩種顏色:0|1,對應:黑|白。若是一種圖片格式對應的二進制位數有n個,那麼它就能夠呈現2^n種顏色。數據庫
JPG最大的特色是有損壓縮。JPG壓縮的體積小,加載快,可是不支持透明。
JPG適用於呈現色彩豐富的圖片。使用JPG呈現大圖,既能夠保住圖片的質量,也不會帶來過於頭疼的圖片體積。是當下比較推崇的方案。
編程
PNG的特色是無損壓縮,質量高,體積大,支持透明。
8位的PNG最多支持256種顏色(2^8),24位的則能夠呈現約1600萬(2^24)種顏色。 PNG通常用於呈現小的Logo,顏色簡單且對比強烈的圖片或背景等。瀏覽器
SVG(可縮放矢量圖),是一種基於XML語法的圖像格式。
SVG的特色是文本文件、體積小、不失真、兼容性好。
可是它也有兩個明顯的侷限性:a.渲染成本比較高,對性能不利;b.SVG存在其它圖片格式沒有的學習成本(它是可編程的)。緩存
Base64的特色是文本文件、依賴編碼、小圖標解決方案。
將Base64格式直接寫入img標籤的src屬性,瀏覽器能夠解析並呈現圖片,從而節省了經過路徑形式所須要的http請求。Base64是做爲雪碧圖的補充而存在的。
通過Base64編碼後,圖片大小會膨脹爲原來的4/3,因此對於大圖來講,沒法使用這種格式。咱們能夠在webpack中使用url-loader對特定大小的圖片進行Base64編碼。性能優化
WebP的特色是圖片質量好、支持透明、能夠像gif同樣支持動態。
可是,雖然集衆多優勢於一身,WebP也有兩個比較明顯的缺點:
a.兼容性很差(目前只有Chrome支持比較好)
b.會增長服務器的負擔
服務器
瀏覽器緩存機制有4個方面,按照獲取資源時請求的優先級排列以下:
1.Memory Cache
2.Service Worker Cache
3.HTTP Cache
4.Push Cache
babel
HTTP緩存分爲強緩存和協商緩存。命中強緩存失敗的狀況下,纔會走協商緩存。
強緩存
強緩存是用http頭中的Expires和Cache-Control兩個字段來控制的。
強緩存中,當請求再次發出時,瀏覽器會根據其中的expires和cache-control判斷目標資源是否「命中」強緩存,若命中則直接從緩存中獲取資源,不會再與服務端發生通訊。強緩存命中返回的HTTP狀態碼爲200。
expires是一個時間戳,接下來若是咱們試圖再次向服務器請求資源,瀏覽器就會先對比本地時間和expires的時間戳,若是本地時間小於expires設定的過時時間,那麼就直接去緩存中取這個資源。
Cache-Control能夠視做是expires的徹底替代方案。在當下的前端實踐裏,咱們繼續使用 expires的惟一目的就是向下兼容。
在 Cache-Control 中,咱們經過max-age來控制資源的有效期。max-age不是一個時間戳,而是一個時間長度(以秒爲單位)。當Cache-Control與expires同時出現時,咱們以Cache-Control爲準。
Cache-Control相應的配置屬性有: max-age=3600|s-maxage=31536000|public|private|no-store|no-cache
客戶端中咱們只考慮max-age。
s-maxage僅在代理服務器中生效,s-maxage就是用於表示cache服務器上(好比 cache CDN)的緩存的有效時間的,並只對public緩存有效。
資源設置了public,那麼它既能夠被瀏覽器緩存,也能夠被代理服務器緩存;若是咱們設置了private,則該資源只能被瀏覽器緩存。private爲默認值。
no-cache繞開了瀏覽器:咱們爲資源設置了no-cache後,每一次發起請求都不會再去詢問瀏覽器的緩存狀況,而是直接向服務端去確認該資源是否過時。
no-store顧名思義就是不使用任何緩存策略。在no-cache的基礎上,它連服務端的緩存確認也繞開了,只容許你直接向服務端發送請求、並下載完整的響應。
協商緩存
協商緩存依賴於服務端與瀏覽器之間的通訊。
協商緩存機制下,瀏覽器須要向服務器去詢問緩存的相關信息,進而判斷是從新發起請求、下載完整的響應,仍是從本地獲取緩存的資源。 若是服務端提示緩存資源未改動(Not Modified),資源會被重定向到瀏覽器緩存,這種狀況下網絡請求對應的狀態碼是304。
協商緩存的實現:從 Last-Modified到Etag Last-Modified是一個時間戳,若是咱們啓用了協商緩存,它會在首次請求時隨着Response Headers返回。隨後咱們每次請求時,會帶上一個叫If-Modified-Since的時間戳字段,它的值正是上一次response返回給它的last-modified值。
Etag 是由服務器爲每一個資源生成的惟一的標識字符串,這個標識字符串是基於文件內容編碼的,只要文件內容不一樣,它們對應的Etag就是不一樣的,反之亦然。所以Etag可以精準地感知文件的變化。
Etag 的生成過程須要服務器額外付出開銷,會影響服務端的性能,這是它的弊端。
Memory Cache是指存在內存中的緩存。從優先級上來講,它是瀏覽器最早嘗試去命中的一種緩存。從效率上來講,它是響應速度最快的一種緩存。
內存緩存是快的,也是「短命」的。它和渲染進程「生死相依」,當進程結束後,也就是tab關閉之後,內存裏的數據也將不復存在。
Service Worker 是一種獨立於主線程以外的Javascript線程。它脫離於瀏覽器窗體,所以沒法直接訪問DOM。
關於Push Cache,只須要了解如下三點便可:
1.Push Cache 是緩存的最後一道防線。瀏覽器只有在Memory Cache、HTTP Cache和 Service Worker Cache 均未命中的狀況下才會去詢問 Push Cache。
2.Push Cache 是一種存在於會話階段的緩存,當 session 終止時,緩存也隨之釋放。
3.不一樣的頁面只要共享了同一個 HTTP2 鏈接,那麼它們就能夠共享同一個 Push Cache。
本地存儲相關的主要有:
1.Cookie
2.Local Storage,Session Storage
3.客戶端的非關係型數據庫:IndexDB
CDN(Content Delivery Network,即內容分發網絡)指的是一組分佈在各個地區的服務器。這些服務器存儲着數據的副本,所以服務器能夠根據哪些服務器與用戶距離最近,來知足數據的請求。
CDN的核心點有兩個,一個是緩存,一個是回源。
緩存就是把資源拷貝一份到CDN服務器上這個過程。
回源就是發現CDN上沒有這個資源(或者說資源過時了),轉頭向根服務器請求這個資源的過程。
同一個域名下的請求會不分青紅皁白地攜帶Cookie,而靜態資源每每並不須要Cookie攜帶什麼認證信息。把靜態資源和主頁面置於不一樣的域名下,完美地避免了沒必要要的Cookie的出現。
客戶端渲染:頁面上呈現的內容,在HTML源文件裏每每找不到,這是客戶端渲染的一個特色。
服務端渲染:服務端渲染的模式下,當用戶第一次請求頁面時,由服務器把須要的組件或頁面渲染成HTML字符串,而後把它返回給客戶端。客戶端拿到手的,是能夠直接渲染而後呈現給用戶的HTML內容,不須要爲了生成DOM內容本身再去跑一遍JS代碼。頁面上呈現的內容,咱們在HTML源文件裏也能找的到。
服務端渲染每每是處於效益的考慮,而不是性能。搜索引擎只會查找頁面內現成的內容,而不會跑JS文件。爲了把現成的內容展現給搜索引擎,須要啓用服務端渲染,加強所謂的SEO。
服務端渲染還能很好的解決首屏渲染的問題。
服務端渲染本質上是本該瀏覽器作的事情,分擔給服務器去作。這樣當資源抵達瀏覽器時,它呈現的速度就快了。
瀏覽器內核決定了瀏覽器解釋網頁語法的方式,瀏覽器內核能夠分紅兩部分:渲染引擎和JS引擎。
渲染引擎包括了HTML解釋器、CSS解釋器、佈局、網絡、存儲、圖形、音視頻、圖片解碼器等零部件。
市面上常見的瀏覽器內核有四種:Trident(IE),Gecko(火狐),Blink(Chrome,Opera),Webkit(Safari)。
渲染過程:簡單來講,渲染引擎根據HTML文件描述構建相應的數學模型,調用瀏覽器各個零部件,從而將網頁資源代碼轉換爲圖像結果,這個過程就是渲染過程。
a.HTML 解釋器:將HTML文檔通過詞法分析輸出DOM樹。
b.CSS 解釋器:解析 CSS 文檔, 生成樣式規則(CSSOM)。
c.圖層佈局計算模塊(Layout):佈局計算每一個對象的精確位置和大小。
d.視圖繪製模塊(Paint):進行具體節點的圖像繪製,將像素渲染到屏幕上。
e.JavaScript引擎:編譯執行Javascript代碼。
渲染過程說白了,首先是基於HTML構建一個DOM樹,這棵DOM樹與CSS解釋器解析出的CSSOM 相結合,就有了佈局渲染樹。最後瀏覽器以佈局渲染樹爲藍本,去計算佈局並繪製圖像,咱們頁面的初次渲染就大功告成了。
須要知道的是CSS引擎查找樣式表,對每條規則都按從右到左的順序去匹配。CSS的優化能夠遵循下面的幾項規則。
1.避免使用通配符(*),只對須要用到的元素進行選擇。
2.關注能夠經過繼承實現的樣式,避免重複匹配重複定義。
3.少用標籤選擇器。
4.減小嵌套,選擇器深度最好不要超過三層。
瀏覽器在構建CSSOM的過程當中,不會渲染任何已處理的內容。CSS資源是阻塞渲染的資源,須要將它儘早、儘快地下載到客戶端,以縮短首次渲染的時間。
1.儘早:將CSS放在head標籤裏。
2.儘快:啓用CDN實現靜態資源加載速度的優化。
JavaScript的做用在於修改,修改網頁的方方面面:內容、樣式以及如何響應用戶交互。JS執行會阻塞CSSOM,也會阻塞DOM。
JS引擎是獨立於渲染引擎存在的。咱們的JS代碼在文檔的何處插入,就在何處執行。 當HTML解析器遇到一個script標籤時,它會暫停渲染過程,將控制權交給JS引擎。JS 引擎對內聯的JS代碼會直接執行,對外部JS文件還要先獲取到腳本、再進行執行。等JS引擎運行完畢,瀏覽器又會把控制權還給渲染引擎,繼續 CSSOM 和 DOM 的構建。
所以與其說是JS把CSS和HTML阻塞了,不如說是JS引擎搶走了渲染引擎的控制權。瀏覽器之因此讓JS阻塞其它的活動,是由於它不知道JS會作什麼改變,擔憂若是不阻止後續的操做,會形成混亂。
JavaScript的三種加載方式:
1.正常模式,哪裏加載,哪裏執行。
2.async模式。async模式下,JS不會阻塞瀏覽器作任何其它的事情。它的加載是異步的,當它加載結束,JS腳本會當即執行。
3.defer模式。defer 模式下,JS的加載是異步的,執行是被推遲的。等整個文檔解析完成、DOMContentLoaded事件即將被觸發時,被標記了defer的JS文件纔會開始依次執行。 從應用的角度來講,通常當咱們的腳本與DOM元素和其它腳本之間的依賴關係不強時,咱們會選用async;當腳本依賴於DOM元素和其它腳本的執行結果時,咱們會選用defer。
事件循環中的異步隊列有兩種:macro(宏任務)隊列和 micro(微任務)隊列。
常見的macro-task好比:setTimeout、setInterval、setImmediate、script(總體代碼)、I/O 操做、UI 渲染等。
常見的micro-task好比: process.nextTick、Promise、MutationObserver等。
迴流:瀏覽器須要從新計算元素的幾何屬性,而後將從新計算的結果繪製出來。
重繪:瀏覽器不須要從新計算元素幾何屬性,佈局位置沒有變。