嗨,送你一張Web性能優化地圖 前端10K面試準備,最完整面試真題分享(含答案)!

我們都知道對於Web應用來說性能很重要。然而性能優化相關的知識卻非常的龐大並且雜亂。對於性能優化需要做些什麼以及性能瓶頸是什麼,通常我們並不清楚(不包括那些對性能優化有豐富經驗的高手)。

事實上關於Web性能有很多可以優化的點,其中涉及到的知識大致可以劃分爲幾類:度量標準、編碼優化、靜態資源優化、交付優化、構建優化、性能監控。

 

 

本文主要介紹性能優化需要做的事以及需要考慮的問題。目的在於給讀者腦海中生成一個宏觀的地圖。

 

前端10K面試準備,最完整面試真題分享(含答案)!

 

不會介紹每個優化項目具體如何操作。PS:後續會有系列文章針對不同優化分類下的具體優化操作進行更詳細的介紹。

1. 度量標準與設定目標

在進行性能優化之前,我們需要爲應用選擇一個正確的度量標準(性能指標)以及設定一個合理的優化目標。

並不是所有指標都同樣重要,這取決於你的應用。最後根據度量標準設定一個現實的目標。

1.1 度量標準

下面是一些值得考慮的指標:

  • 首次有效繪製(First Meaningful Paint,簡稱FMP,當主要內容呈現在頁面上)
  • 英雄渲染時間(Hero Rendering Times,度量用戶體驗的新指標,當用戶最關心的內容渲染完成)
  • 可交互時間(Time to Interactive,簡稱TTI,指頁面佈局已經穩定,關鍵的頁面字體是可見的,並且主進程可用於處理用戶輸入,基本上用戶可以點擊UI並與其交互)
  • 輸入響應(Input responsiveness,界面響應用戶輸入所需的時間)
  • 感知速度指數(Perceptual Speed Index,簡稱PSI,測量頁面在加載過程中視覺上的變化速度,分數越低越好)
  • 自定義指標,由業務需求和用戶體驗來決定。

FMP與英雄渲染時間非常相似,但它們不一樣的地方在於FMP不區分內容是否有用,不區分渲染出的內容是否是用戶關心的。

1.2 設定目標

  • 100毫秒的界面響應時間與60FPS
  • 速度指標(Speed Index)小於1250ms
  • 3G網絡環境下可交互時間小於5s
  • 重要文件的大小預算小於170kb

以上四種指標的設定都有據可循。詳細信息請查看RAIL性能模型。

2. 編碼優化

編碼優化涉及到應用的運行時性能,本小節介紹幾個可以提升程序運行時性能的建議。

2.1 數據讀取速度

事實上數據訪問速度有快慢之分,下面列出幾個影響數據訪問速度的因素:

  • 字面量與局部變量的訪問速度最快,數組元素和對象成員相對較慢
  • 變量從局部作用域到全局作用域的搜索過程越長速度越慢
  • 對象嵌套的越深,讀取速度就越慢
  • 對象在原型鏈中存在的位置越深,找到它的速度就越慢
推薦的做法是緩存對象成員值。將對象成員值緩存到局部變量中會加快訪問速度

2.2 DOM

應用在運行時,性能的瓶頸主要在於DOM操作的代價非常昂貴,下面列出一些關於DOM操作相關提升性能的建議:

  • 在JS中對DOM進行訪問的代價非常高。請儘可能減少訪問DOM的次數(建議緩存DOM屬性和元素、把DOM集合的長度緩存到變量中並在迭代中使用。讀變量比讀DOM的速度要快很多。)
  • 重排與重繪的代價非常昂貴。如果操作需要進行多次重排與重繪,建議先讓元素脫離文檔流,處理完畢後再讓元素迴歸文檔流,這樣瀏覽器只會進行兩次重排與重繪(脫離時和迴歸時)。
  • 善於使用事件委託

2.3 流程控制

下面列出一些流程控制相關的一些可以略微提升性能的細節,這些細節在大型開源項目中大量運用(例如Vue):

  • 避免使用for...in(它能枚舉到原型,所以很慢)
  • 在JS中倒序循環會略微提升性能
  • 減少迭代的次數
  • 基於循環的迭代比基於函數的迭代快8倍
  • 用Map表代替大量的if-else和switch會提升性能

3. 靜態資源優化

Web應用的運行離不開靜態資源,所以對靜態資源的優化至關重要。

3.1 使用Brotli或Zopfli進行純文本壓縮

在最高級別的壓縮下Brotli會非常慢(但較慢的壓縮最終會得到更高的壓縮率)以至於服務器在等待動態資源壓縮的時間會抵消掉高壓縮率帶來的好處,但它非常適合靜態文件壓縮,因爲它的解壓速度很快。

使用Zopfli壓縮可以比Zlib的最大壓縮提升3%至8%。

 

3.2 圖片優化

儘可能通過srcset,sizes和<picture>元素使用響應式圖片。還可以通過<picture>元素使用WebP格式的圖像。

響應式圖片可能大家未必聽說過,但響應式佈局大家肯定都聽說過。響應式圖片與響應式佈局類似,它可以在不同屏幕尺寸與分辨率的設備上都能良好工作(比如自動切換圖片大小、自動裁切圖片等)。

當然,如果您不滿足這種尺度的優化,還可以對圖片進行更深層次的優化。例如:模糊圖片中不重要的部分以減小文件大小、使用自動播放與循環的HTML5視頻替換GIF圖,因爲視頻比GIF文件還小(好消息是未來可以通過img標籤加載視頻)。

4. 交付優化

交付優化指的是對頁面加載資源以及用戶與網頁之間的交付過程進行優化。

4.1 異步無阻塞加載JS

JS的加載與執行會阻塞頁面渲染,可以將Script標籤放到頁面的最底部。但是更好的做法是異步無阻塞加載JS。有多種無阻塞加載JS的方法:defer、async、動態創建script標籤、使用XHR異步請求JS代碼並注入到頁面。

但更推薦的做法是使用defer或async。如果使用defer或async請將Script標籤放到head標籤中,以便讓瀏覽器更早地發現資源並在後臺線程中解析並開始加載JS。

4.2 使用Intersection Observer實現懶加載

懶加載是一個比較常用的性能優化手段,下面列出了一些常用的做法:

  • 可以通過Intersection Observer延遲加載圖片、視頻、廣告腳本、或任何其他資源。
  • 可以先加載低質量或模糊的圖片,當圖片加載完畢後再使用完整版圖片替換它。
延遲加載所有體積較大的組件、字體、JS、視頻或Iframe是一個好主意

4.3 優先加載關鍵的CSS

CSS資源的加載對瀏覽器渲染的影響很大,默認情況下瀏覽器只有在完成<head>標籤中CSS的加載與解析之後纔會渲染頁面。如果CSS文件過大,用戶就需要等待很長的時間才能看到渲染結果。針對這種情況可以將首屏渲染必須用到的CSS提取出來內嵌到<head>中,然後再將剩餘部分的CSS用異步的方式加載。可以通過Critical做到這一點。

4.4 資源提示(Resource Hints)

Resource Hints(資源提示)定義了HTML中的Link元素與dns-prefetch、preconnect、prefetch與prerender之間的關係。它可以幫助瀏覽器決定應該連接到哪些源,以及應該獲取與預處理哪些資源來提升頁面性能。

4.4.1 dns-prefetch

dns-prefetch可以指定一個用於獲取資源所需的源(origin),並提示瀏覽器應該儘可能早的解析。

<link rel="dns-prefetch" href="//example.com">

4.4.2 preconnect

preconnect用於啓動預鏈接,其中包含DNS查找,TCP握手,以及可選的TLS協議,允許瀏覽器減少潛在的建立連接的開銷。

<link rel="preconnect" href="//example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>

4.4.3 prefetch

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選項都是可選的。

4.4.4 prerender

prerender用於標識下一個導航可能需要的資源。瀏覽器會獲取並執行,一旦將來請求該資源,瀏覽器可以提供更快的響應。

<link rel="prerender" href="//example.com/next-page.html">

瀏覽器將預加載目標頁面相關的資源並執行來預處理HTML響應。

4.5 Preload

通過一個現有元素(例如: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

4.6 快速響應的用戶界面

PSI(Perceptual Speed Index,感知速度指數)是提升用戶體驗的重要指標,讓用戶感覺到頁面的反饋比沒有反饋體驗要好很多。

可以嘗試使用骨架屏或添加一些Loading過渡動畫提示用戶體驗。

輸入響應(Input responsiveness)指標同樣重要,甚至更重要。試想,用戶點擊了網頁後缺毫無反應會是什麼心情。JS的單線程大家已經不能再熟悉,這意味着當JS在運行時用戶界面處於“鎖定”狀態,所以JS同步執行的時間越長,用戶等待響應的時間也就越長。

據調查,JS執行100毫秒以上用戶就會明顯覺得網頁變卡了。所以要嚴格限制每個JS任務執行時間不能超過100毫秒。

解決方案是可以將一個大任務拆分成多個小任務分佈在不同的Macrotask中執行(通俗的說是將大的JS任務拆分成多個小任務異步執行)。或者使用WebWorkers,它可以在UI線程外執行JS代碼運算,不會阻塞UI線程,所以不會影響用戶體驗。

應用越複雜,主動管理UI線程就越重要

5. 構建優化

現代前端應用都需要有構建的過程,項目在構建過程中是否進行了合理的優化,會對Web應用的性能有着巨大的影響。例如:影響構建後文件的體積、代碼執行效率、文件加載時間、首次有效繪製指標等。

5.1 使用預編譯

拿Vue舉例,如果您使用單文件組件開發項目,組件會在編譯階段將模板編譯爲渲染函數。最終代碼被執行時可以直接執行渲染函數進行渲染。而如果您沒有使用單文件組件預編譯代碼,而是在網頁中引入vue.min.js,那麼應用在運行時需要先將模板編譯成渲染函數,然後再執行渲染函數進行渲染。相比預編譯,多了模板編譯的步驟,所以會浪費很多性能。

5.2 使用 Tree-shaking、Scope hoisting、Code-splitting

Tree-shaking是一種在構建過程中清除無用代碼的技術。使用Tree-shaking可以減少構建後文件的體積。

目前Webpack與Rollup都支持Scope Hoisting。它們可以檢查import鏈,並儘可能的將散亂的模塊放到一個函數中,前提是不能造成代碼冗餘。所以只有被引用了一次的模塊纔會被合併。使用Scope Hoisting可以讓代碼體積更小並且可以降低代碼在運行時的內存開銷,同時它的運行速度更快。前面2.1節介紹了變量從局部作用域到全局作用域的搜索過程越長執行速度越慢,Scope Hoisting可以減少搜索時間。

code-splitting是Webpack中最引人注目的特性之一。此特性能夠把代碼分離到不同的bundle中,然後可以按需加載或並行加載這些文件。code-splitting可以用於獲取更小的bundle,以及控制資源加載優先級,如果使用合理,會極大影響加載時間。

5.3 服務端渲染(SSR)

單頁應用需要等JS加載完畢後在前端渲染頁面,也就是說在JS加載完畢並開始執行渲染操作前的這段時間裏瀏覽器會產生白屏。

服務端渲染(Server Side Render,簡稱SSR)的意義在於彌補主要內容在前端渲染的成本,減少白屏時間,提升首次有效繪製的速度。可以使用服務端渲染來獲得更快的首次有效繪製。

比較推薦的做法是:使用服務端渲染靜態HTML來獲得更快的首次有效繪製,一旦JavaScript加載完畢再將頁面接管下來。

5.4 使用import函數動態導入模塊

使用import函數可以在運行時動態地加載ES2015模塊,從而實現按需加載的需求。

這種優化在單頁應用中變得尤爲重要,在切換路由的時候動態導入當前路由所需的模塊,會避免加載冗餘的模塊(試想如果在首次加載頁面時一次性把整個站點所需要的所有模塊都同時加載下來會加載多少非必須的JS,應該儘可能的讓加載的JS更小,只在首屏加載需要的JS)。

使用靜態import導入初始依賴模塊。其他情況下使用動態import按需加載依賴

5.5 使用HTTP緩存頭

正確設置expires,cache-control和其他HTTP緩存頭。

推薦使用Cache-control: immutable避免重新驗證。

6. 其他

其他一些值得考慮的優化點:

  • HTTP2
  • 使用最高級的CDN(付費的比免費的強的多)
  • 優化字體
  • 其他垂直領域的性能優化

7. 性能監控

最後,你可能需要一個性能檢測工具來持續監視網站的性能。

8. 總結

最後用一張圖來總結這篇文章所表達的內容,感謝@anjia幫忙畫的這張圖。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章