咱們都知道對於Web應用來講性能很重要。然而性能優化相關的知識卻很是的龐大而且雜亂。對於性能優化須要作些什麼以及性能瓶頸是什麼,一般咱們並不清楚。javascript
不包括那些對性能優化有豐富經驗的高手css
事實上關於Web性能有不少能夠優化的點,其中涉及到的知識大體能夠劃分爲幾類:度量標準、編碼優化、靜態資源優化、交付優化、構建優化、性能監控。html
圖1. 性能優化分類前端
本文主要介紹性能優化須要作的事以及須要考慮的問題。目的在於給讀者腦海中生成一個宏觀的地圖。vue
不會介紹每一個優化項目具體如何操做。PS:後續會有系列文章針對不一樣優化分類下的具體優化操做進行更詳細的介紹。java
在進行性能優化以前,咱們須要爲應用選擇一個正確的度量標準(性能指標)以及設定一個合理的優化目標。git
並非全部指標都一樣重要,這取決於你的應用。最後根據度量標準設定一個現實的目標。github
下面是一些值得考慮的指標:web
FMP與英雄渲染時間很是類似,但它們不同的地方在於FMP不區份內容是否有用,不區分渲染出的內容是不是用戶關心的。面試
想了解更多「性能指標」及更多「Web性能領域的專業術語」能夠看個人另外一篇文章:《Web性能領域常見的專業術語》
以上四種指標的設定都有據可循。詳細信息請查看RAIL性能模型。
編碼優化涉及到應用的運行時性能,本小節介紹幾個能夠提高程序運行時性能的建議。
事實上數據訪問速度有快慢之分,下面列出幾個影響數據訪問速度的因素:
推薦的作法是緩存對象成員值。將對象成員值緩存到局部變量中會加快訪問速度
應用在運行時,性能的瓶頸主要在於DOM操做的代價很是昂貴,下面列出一些關於DOM操做相關提高性能的建議:
下面列出一些流程控制相關的一些能夠略微提高性能的細節,這些細節在大型開源項目中大量運用(例如Vue):
for...in
(它能枚舉到原型,因此很慢)if-else
和switch
會提高性能Web應用的運行離不開靜態資源,因此對靜態資源的優化相當重要。
Brotli
或Zopfli
進行純文本壓縮在最高級別的壓縮下Brotli會很是慢(但較慢的壓縮最終會獲得更高的壓縮率)以致於服務器在等待動態資源壓縮的時間會抵消掉高壓縮率帶來的好處,但它很是適合靜態文件壓縮,由於它的解壓速度很快。
使用Zopfli壓縮能夠比Zlib的最大壓縮提高3%至8%。
儘量經過srcset
,sizes
和<picture>
元素使用響應式圖片。還能夠經過<picture>
元素使用WebP格式的圖像。
響應式圖片可能你們未必據說過,但響應式佈局你們確定都據說過。響應式圖片與響應式佈局相似,它能夠在不一樣屏幕尺寸與分辨率的設備上都能良好工做(好比自動切換圖片大小、自動裁切圖片等)。
固然,若是您不知足這種尺度的優化,還能夠對圖片進行更深層次的優化。例如:模糊圖片中不重要的部分以減少文件大小、使用自動播放與循環的HTML5視頻替換GIF圖,由於視頻比GIF文件還小(好消息是將來能夠經過img
標籤加載視頻)。
更多圖片優化能夠看個人另外一篇文章:《圖像優化原理》
交付優化指的是對頁面加載資源以及用戶與網頁之間的交付過程進行優化。
JS的加載與執行會阻塞頁面渲染,能夠將Script標籤放到頁面的最底部。可是更好的作法是異步無阻塞加載JS。有多種無阻塞加載JS的方法:defer
、async
、動態建立script
標籤、使用XHR異步請求JS代碼並注入到頁面。
但更推薦的作法是使用defer
或async
。若是使用defer
或async
請將Script標籤放到head
標籤中,以便讓瀏覽器更早地發現資源並在後臺線程中解析並開始加載JS。
Intersection Observer
實現懶加載懶加載是一個比較經常使用的性能優化手段,下面列出了一些經常使用的作法:
Intersection Observer
延遲加載圖片、視頻、廣告腳本、或任何其餘資源。延遲加載全部體積較大的組件、字體、JS、視頻或Iframe是一個好主意
CSS資源的加載對瀏覽器渲染的影響很大,默認狀況下瀏覽器只有在完成<head>
標籤中CSS的加載與解析以後纔會渲染頁面。若是CSS文件過大,用戶就須要等待很長的時間才能看到渲染結果。針對這種狀況能夠將首屏渲染必須用到的CSS提取出來內嵌到<head>
中,而後再將剩餘部分的CSS用異步的方式加載。能夠經過Critical作到這一點。
Resource Hints(資源提示)定義了HTML中的Link元素與dns-prefetch
、preconnect
、prefetch
與prerender
之間的關係。它能夠幫助瀏覽器決定應該鏈接到哪些源,以及應該獲取與預處理哪些資源來提高頁面性能。
dns-prefetch
能夠指定一個用於獲取資源所需的源(origin),並提示瀏覽器應該儘量早的解析。
<link rel="dns-prefetch" href="//example.com">
複製代碼
preconnect
用於啓動預連接,其中包含DNS查找,TCP握手,以及可選的TLS協議,容許瀏覽器減小潛在的創建鏈接的開銷。
<link rel="preconnect" href="//example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>
複製代碼
Prefetch
用於標識下一個導航可能須要的資源。瀏覽器會獲取該資源,一旦未來請求該資源,瀏覽器能夠提供更快的響應。
<link rel="prefetch" href="//example.com/next-page.html" as="html" crossorigin="use-credentials">
<link rel="prefetch" href="/library.js" as="script">
複製代碼
瀏覽器不會預處理、不會自動執行、不會將其應用於當前上下文。
as
與crossorigin
選項都是可選的。
prerender
用於標識下一個導航可能須要的資源。瀏覽器會獲取並執行,一旦未來請求該資源,瀏覽器能夠提供更快的響應。
<link rel="prerender" href="//example.com/next-page.html">
複製代碼
瀏覽器將預加載目標頁面相關的資源並執行來預處理HTML響應。
經過一個現有元素(例如:img
,script
,link
)聲明資源會將獲取與執行耦合在一塊兒。然而應用可能只是想要先獲取資源,當知足某些條件時再執行資源。
Preload提供了預獲取資源的能力,能夠將獲取資源的行爲從資源執行中分離出來。所以,Preload能夠構建自定義的資源加載與執行。
例如,應用可使用Preload進行CSS資源的預加載、而且同時具有:高優先級、不阻塞渲染等特性。而後應用程序在合適的時間使用CSS資源:
<!-- 經過聲明性標記預加載 CSS 資源 -->
<link rel="preload" href="/styles/other.css" as="style">
<!-- 或,經過JavaScript預加載 CSS 資源 -->
<script> var res = document.createElement("link"); res.rel = "preload"; res.as = "style"; res.href = "styles/other.css"; document.head.appendChild(res); </script>
複製代碼
<!-- 使用HTTP頭預加載 -->
Link: <https://example.com/other/styles.css>; rel=preload; as=style
複製代碼
PSI(Perceptual Speed Index,感知速度指數)是提高用戶體驗的重要指標,讓用戶感受到頁面的反饋比沒有反饋體驗要好不少。
能夠嘗試使用骨架屏或添加一些Loading過渡動畫提示用戶體驗。
輸入響應(Input responsiveness)指標一樣重要,甚至更重要。試想,用戶點擊了網頁後缺毫無反應會是什麼心情。JS的單線程你們已經不能再熟悉,這意味着當JS在運行時用戶界面處於「鎖定」狀態,因此JS同步執行的時間越長,用戶等待響應的時間也就越長。
據調查,JS執行100毫秒以上用戶就會明顯以爲網頁變卡了。因此要嚴格限制每一個JS任務執行時間不能超過100毫秒。
解決方案是能夠將一個大任務拆分紅多個小任務分佈在不一樣的Macrotask中執行(通俗的說是將大的JS任務拆分紅多個小任務異步執行)。或者使用WebWorkers,它能夠在UI線程外執行JS代碼運算,不會阻塞UI線程,因此不會影響用戶體驗。
應用越複雜,主動管理UI線程就越重要
現代前端應用都須要有構建的過程,項目在構建過程當中是否進行了合理的優化,會對Web應用的性能有着巨大的影響。例如:影響構建後文件的體積、代碼執行效率、文件加載時間、首次有效繪製指標等。
拿Vue舉例,若是您使用單文件組件開發項目,組件會在編譯階段將模板編譯爲渲染函數。最終代碼被執行時能夠直接執行渲染函數進行渲染。而若是您沒有使用單文件組件預編譯代碼,而是在網頁中引入vue.min.js
,那麼應用在運行時須要先將模板編譯成渲染函數,而後再執行渲染函數進行渲染。相比預編譯,多了模板編譯的步驟,因此會浪費不少性能。
Tree-shaking是一種在構建過程當中清除無用代碼的技術。使用Tree-shaking能夠減小構建後文件的體積。
目前Webpack與Rollup都支持Scope Hoisting
。它們能夠檢查import
鏈,並儘量的將散亂的模塊放到一個函數中,前提是不能形成代碼冗餘。因此只有被引用了一次的模塊纔會被合併。使用Scope Hoisting
可讓代碼體積更小而且能夠下降代碼在運行時的內存開銷,同時它的運行速度更快。前面2.1節介紹了變量從局部做用域到全局做用域的搜索過程越長執行速度越慢,Scope Hoisting
能夠減小搜索時間。
code-splitting
是Webpack中最引人注目的特性之一。此特性可以把代碼分離到不一樣的bundle
中,而後能夠按需加載或並行加載這些文件。code-splitting
能夠用於獲取更小的bundle
,以及控制資源加載優先級,若是使用合理,會極大影響加載時間。
單頁應用須要等JS加載完畢後在前端渲染頁面,也就是說在JS加載完畢並開始執行渲染操做前的這段時間裏瀏覽器會產生白屏。
服務端渲染(Server Side Render,簡稱SSR)的意義在於彌補主要內容在前端渲染的成本,減小白屏時間,提高首次有效繪製的速度。可使用服務端渲染來得到更快的首次有效繪製。
比較推薦的作法是:使用服務端渲染靜態HTML來得到更快的首次有效繪製,一旦JavaScript加載完畢再將頁面接管下來。
import
函數動態導入模塊使用import
函數能夠在運行時動態地加載ES2015模塊,從而實現按需加載的需求。
這種優化在單頁應用中變得尤其重要,在切換路由的時候動態導入當前路由所需的模塊,會避免加載冗餘的模塊(試想若是在首次加載頁面時一次性把整個站點所須要的全部模塊都同時加載下來會加載多少非必須的JS,應該儘量的讓加載的JS更小,只在首屏加載須要的JS)。
使用靜態
import
導入初始依賴模塊。其餘狀況下使用動態import
按需加載依賴
正確設置expires
,cache-control
和其餘HTTP緩存頭。
推薦使用Cache-control: immutable
避免從新驗證。
其餘一些值得考慮的優化點:
最後,你可能須要一個性能檢測工具來持續監視網站的性能。
最後用一張圖來總結這篇文章所表達的內容。
圖2. 總結這篇文章更多性能領域的高質量文章請狠狠的點擊我!
如今是校招季,阿里巴巴淘系前端正在幫助21屆同窗們收割大廠offer。爲此,咱們專門建立了交流羣,在這裏能夠提問題,交流面試經驗,咱們也會在這裏提供簡歷輔導和答疑解惑等服務,
入羣方式: