前端效能優化基礎

Accumulate_HangZhou發表於2019-01-05

前端的效能優化是一個龐大繁雜的領域,涉及到許許多多方面,包括網路傳輸層面、客戶端載入渲染,乃至伺服器端的優化等等。這些大的方向下,又有非常多小的分支。所以,前端的效能優化可以說是許多雜項的集合,而且目前來說,並沒有非常成體系的這樣一套東西。
本文基於掘金小冊《前端效能優化原理與實踐》,是自己的學習總結,並在此基礎上做了一些個人的擴充和提煉,用於形成自己的前端效能優化的相關知識地圖。

一、構建工具的優化(Webpack)

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的按需載入功能。

#Gzip壓縮原理

在請求頭(Request Headers)中新增accept-encoding:gzip;可以開啟Gzip壓縮。
Gzip壓縮通常能夠減小70%左右的體積。它的原理是在一個檔案中找出那些重複出現的字串,臨時替換它們,從而使整個檔案變小。

二、圖片相關優化

當下web應用比較廣泛的圖片格式有:JPEG/JPG,PNG,WebP,Base64,SVG等。
在計算機中,畫素用二進位制位數來表示。一個畫素對應的二進位制位數越多,它可以表示的顏色種類就越多。一個二進位制位表示兩種顏色:0|1,對應:黑|白。如果一種圖片格式對應的二進位制位數有n個,那麼它就可以呈現2^n種顏色。

#JPEG/JPG

JPG最大的特點是有失真壓縮。JPG壓縮的體積小,載入快,但是不支援透明。
JPG適用於呈現色彩豐富的圖片。使用JPG呈現大圖,既可以保住圖片的質量,也不會帶來過於頭疼的圖片體積。是當下比較推崇的方案。

#PNG-8與PNG-24

PNG的特點是無失真壓縮,質量高,體積大,支援透明。
8位的PNG最多支援256種顏色(2^8),24位的則可以呈現約1600萬(2^24)種顏色。 PNG一般用於呈現小的Logo,顏色簡單且對比強烈的圖片或背景等。

#SVG

SVG(可縮放向量圖),是一種基於XML語法的影象格式。
SVG的特點是文字檔案、體積小、不失真、相容性好。
但是它也有兩個明顯的侷限性:a.渲染成本比較高,對效能不利;b.SVG存在其它圖片格式沒有的學習成本(它是可程式設計的)。

#Base64

Base64的特點是文字檔案、依賴編碼、小圖示解決方案。
將Base64格式直接寫入img標籤的src屬性,瀏覽器可以解析並呈現圖片,從而節省了通過路徑形式所需要的http請求。Base64是作為雪碧圖的補充而存在的。
經過Base64編碼後,圖片大小會膨脹為原來的4/3,所以對於大圖來說,無法使用這種格式。我們可以在webpack中使用url-loader對特定大小的圖片進行Base64編碼。

#WebP

WebP的特點是圖片質量好、支援透明、可以像gif一樣支援動態。
但是,雖然集眾多優點於一身,WebP也有兩個比較明顯的缺點:
a.相容性不好(目前只有Chrome支援比較好)
b.會增加伺服器的負擔

三、瀏覽器快取機制

瀏覽器快取機制有4個方面,按照獲取資源時請求的優先順序排列如下:
1.Memory Cache
2.Service Worker Cache
3.HTTP Cache
4.Push Cache

#HTTP快取機制

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

Memory Cache是指存在記憶體中的快取。從優先順序上來說,它是瀏覽器最先嚐試去命中的一種快取。從效率上來說,它是響應速度最快的一種快取。
記憶體快取是快的,也是“短命”的。它和渲染程式“生死相依”,當程式結束後,也就是tab關閉以後,記憶體裡的資料也將不復存在。

#Service Worker Cache

Service Worker 是一種獨立於主執行緒之外的Javascript執行緒。它脫離於瀏覽器窗體,因此無法直接訪問DOM。

#Push Cache

關於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的快取與回源機制

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引擎查詢樣式表,對每條規則都按從右到左的順序去匹配。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。

八、Event Loop與非同步更新策略

事件迴圈中的非同步佇列有兩種:macro(巨集任務)佇列和 micro(微任務)佇列。
常見的macro-task比如:setTimeout、setInterval、setImmediate、script(整體程式碼)、I/O 操作、UI 渲染等。
常見的micro-task比如: process.nextTick、Promise、MutationObserver等。

九、迴流(Reflow)與重繪(Repaint)

迴流:瀏覽器需要重新計算元素的幾何屬性,然後將重新計算的結果繪製出來。
重繪:瀏覽器不需要重新計算元素幾何屬性,佈局位置沒有變。

十、效能監測

#Chrome devtools Performance的使用
#LightHouse外掛的使用(可用於生成頁面效能分析報告)
#可程式設計的效能上報方案:window.performance

十一、其它常用優化方案

#圖片的Lazy-Load
#事件的節流(throttle)與防抖(debounce)的應用

相關文章