前端性能優化那些事

本文原創:gaoruixiacss

一. 開篇

提起前端性能優化,你們會想到哪些內容呢?html

相信不一樣的人給出的答案不一樣,關於性能優化沒有標準答案,它更像是一個摸索的過程,本篇文章給出從頁面加載的各個過程分析可優化的點前端

二. 經常使用性能優化指標

下面這張圖是一個頁面從打開到加載完成經歷的各個階段webpack

webLoadTiming.png

  • 瀏覽器得到各個階段的時間耗時的APIweb

    - Timing標準:window.performance.timing
    - Timing2標準: window.performance.getEntriesByType('navigation')[0]。
    複製代碼

    Timing目前瀏覽器已再也不提供維護支持,Timing2獲取的精度更加準確(比飛秒還精確,爲10的-17次方秒),但兼容性尚且沒有那麼好(主要是iOS11纔開始支持,其餘瀏覽器都基本沒問題了,IE9咱們就讓它見鬼去吧)。在京麥插件性能統計中,咱們優先判斷是否支持後者,除非後者不支持才使用前者。ajax

  • 白屏時間json

    從navigatorStart到responseEnd這段時間算做服務器時間
    從瀏覽器開始加載頁面到首次出現內容以前的這段時間(從頁面發送一個頁面URL請求出去,到服務器返回這個HTML的文本內容)後端

    計算方式
    - Timing: performance.timing.responseEnd - performance.timing.navigationStart
    - Timing2: performance.getEntriesByType('navigation')[0].responseStart
    複製代碼
  • 瀏覽器渲染時間瀏覽器

    從responseEnd到loadEventEnd這段時間,包括CSS、JavaScript、Image等資源的加載耗時緩存

    計算方式
    - Timing: performance.timing.loadEventEnd - performance.timing.responseEnd
    - Timing2: performance.getEntriesByType('navigation')[0].loadEventEnd - performance.getEntriesByType('navigation')[0].responseStart
    複製代碼
  • 整頁時間

    從瀏覽器開始加載頁面到整個頁面加載完畢(最明顯的標識就是移動端瀏覽器進度條讀完了,pc端瀏覽器就是當前頁籤前面的loading消失了)

    計算方式
     - Timing: performance.timing.loadEventEnd - performance.timing.navigationStart
     - Timing2: performance.getEntriesByType('navigation')[0].loadEventEnd
    複製代碼

使用Chrome調試工具performance查看瀏覽器請求一個頁面到渲染完成的過程

能夠打開瀏覽器控制檯,切換到Performance,點擊刷新按鈕,等頁面加載完成

webLoad03.jpg

三. 影響整頁時間的因素

先了解下獲得html文本內容後頁面的加載渲染流程(以webkit主流程爲例)

webLoad01.jpg

  • 渲染流程有四個主要步驟

    1. 解析HTML生成DOM樹 - 渲染引擎首先解析HTML文檔,生成DOM樹
    2. 構建Render樹 - 接下來不論是內聯式,外聯式仍是嵌入式引入的CSS樣式會被解析生成CSSOM樹,根據DOM樹與CSSOM樹生成另一棵用於渲染的樹-渲染樹(Render tree),
    3. 佈局Render樹 - 而後對渲染樹的每一個節點進行佈局處理,肯定其在屏幕上的顯示位置
    4. 繪製Render樹 - 最後遍歷渲染樹並用UI後端層將每個節點繪製出來

    以上步驟是一個漸進的過程,爲了提升用戶體驗,渲染引擎試圖儘量快的把結果顯示給最終用戶。它不會等到全部HTML都被解析完才建立並佈局渲染樹。它會在從網絡層獲取文檔內容的同時把已經接收到的局部內容先展現出來。

  • 阻塞渲染的因素

    沒有js的理想狀況下,html與css會並行解析,分別生成DOM與CSSOM,而後合併成Render Tree,進入Rendering Pipeline; 但若是有js,css加載會阻塞後面js語句的執行,而(同步)js腳本執行會阻塞其後的DOM解析(因此一般會把css放在頭部,js放在body尾)

    • CSS的阻塞狀況

      • css不會阻塞DOM樹的解析
      • css會阻塞DOM樹的渲染
      • css會阻塞後面js語句的執行
    • JS的阻塞狀況

      • JS阻塞DOM解析,但瀏覽器會"偷看"DOM,預先下載相關資源
      • 瀏覽器遇到script且沒有defer或async屬性的標籤時,會觸發頁面渲染,於是若是前面CSS資源還沒有加載完畢時,瀏覽器會等待它加載完畢在執行腳本

webLoad02.jpg

藍色線表明網絡讀取,紅色線表明執行時間,這倆都是針對腳本的;綠色線表明HTML解析
複製代碼
  • 由上能夠得出影響整頁時間的因素

    1. CSS、JS、圖片等資源加載
    2. CSS、JS文件放置位置,避免阻塞DOM解析和渲染
    3. ajax同步請求
    4. jsonp請求
    5. 頁面加載過程當中JS發送圖片請求

四. 優化分析

使用Chrome調試工具performance

下圖中,在NetWork資源加載瀑布圖中咱們能夠查看是主要是哪些資源卡住了頁面的整頁時間,發送結束時間均可以觀察到。以後再根據哪一個請求是瓶頸,對那個請求進行分析

webLoad04.jpg

使用Chrome插件pagespeed和Lighthouse

  • 使用插件pagespeed

    首先去安裝,安裝完pagespeed以後,打開你要調試的網頁,打開控制檯的pagespeed,而後點擊左上角的ANALYZE按鈕,開始分析頁面性能狀況

pageSpeed.jpg

  • 插件Lighthouse相似,這裏不詳細介紹了

  • 對於整頁時間較長的狀況,須要分析具體是在哪一個階段致使整頁時間比較長,能夠從上面整頁時間的說明裏的各個階段去分析

  • 整頁時間主要在dom解析耗時上,這段時間是從HTML開始解析,到JS所有執行完畢,這之中包含了CSS、JS等資源的加載,須要去優化HTML解析這段時間。

五. 各階段優化手段

經常使用性能優化指標咱們知道了頁面加載總共分紅下面這幾個階段:

重定向時間 -> DNS緩存查詢時間 -> DNS查詢時間 -> TCP鏈接時間 -> Request請求時間 -> Response請求響應時間 -> DOM解析時間 -> DOM渲染時間 -> Load事件執行時間

下面來看下各階段的優化手段

1. 重定向時間

這個很簡單,就是請求到正確的地址上便可。
常常會出現的狀況是:

  • 將index.html的文件地址請求寫成這樣:https://xx.com/xx,這樣會致使瀏覽器重定向到https://xx.com/xx/
  • 將http請求重定向到https上 這種狀況注意下便可

2. DNS緩存查詢時間

這個是瀏覽器作的處理,如Chrome瀏覽器對DNS的緩存時間是1分鐘。

這個階段咱們沒法優化。

3. DNS查詢時間

HTML的請求的DNS查詢時間咱們沒法縮短,但對於裏面的其餘域名的請求,咱們能夠提早進行DNS查詢,減小資源或者接口的請求時間。

在HTMl的頭部head中加上DNS預查詢便可

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

通常對CDN須要用到的域名作解析便可。如下是CDN的各個域名:

4. TCP鏈接時間

TCP鏈接時間主要在3次握手中,縮短這個時間就是拉近用戶和服務器機房的距離,對於接口的請求這個很難作到,但對於靜態資源的TCP鏈接時間,咱們能夠經過CDN全國節點縮短用戶與服務器之間的距離。

另外,咱們還能夠用HTTP2的keep-alive來保持長鏈接,這個CDN服務器默認是開啓的,對於接口服務器也能夠進行開啓,雖然異步Ajax請求並不影響整頁時間,但能讓用戶早點看到內容,何樂而不爲。

5. Request請求時間 和 Response請求響應時間

在Request請求中,請求返回的時間取決於服務器的處理時間。

對於先後端耦合的項目或者SSR的項目,由於須要處理邏輯,拿出數據再動態構建HTML文本,以後再將HTML文本返回給瀏覽器,那這段時間主要在於處理邏輯上。

對於先後端分離的項目,由於返回的是一個空的HTML文本,數據都是等JS調用Ajax獲取的,因此HTML的Request請求時間很短。但這樣咱們得處理Ajax請求,它也有Request請求的時間,這個也得看怎麼在服務端進行性能提高。

雖然Ajax請求不計算在整頁時間中,但也別爲了縮短整頁時間而選擇先後端分離,這個並非它的優點所在。用戶關心的是頁面何時加載完,這其中包含數據何時展示,因此別爲了整頁時間的優化而優化,而應該關心用戶體驗,就算是Ajax接口也應該考慮怎麼提高性能。

並且使用先後端分離其實會加長頁面的白屏時間,這個也是一個衡量頁面性能的一個重要指標,白屏時間能夠經過添加骨架屏來優化。

對於靜態資源的請求,通常資源越小,請求越快(也受服務器帶寬的影響)。能夠經過減少靜態資源(JS、CSS、圖片等)請求的大小從而來縮短請求響應時間。

6. DOM解析時間

這段時間從服務器響應返回HTML文本,瀏覽器開始按照從上到下的執行順序解析HTML文本,到執行完HTML中的全部JS,這段時間咱們將其稱爲DOM解析時間。因此優化這段時間在於縮短HTML中的CSS文件和JS文件的請求時間和執行時間,以及縮短HTML的解析時間。

縮短CSS文件和JS文件的請求時間

能夠從如下幾方面來作

  • 靜態資源放在CDN上
  • 壓縮文件(代碼混淆壓縮,Gzip)
  • js文件拆分按需加載(webpack合理配置, 可以使用webpack-bundle-analyzer來分析打包)

縮短HTML的解析時間

  • 從前面影響整頁時間的因素中咱們能夠知道,CSS會阻塞HTMl的渲染和JS的執行,JS會阻塞HTML的解析和渲染,所以爲了不阻塞,要嚴格遵照CSS文件放在head頭部,JS文件放在body尾部
  • 減小DOM樹的嵌套深度,這個屬於代碼層次的細節優化,通常在於代碼書寫習慣上,這個很差優化
  • 優化JS代碼執行邏輯,避免耗時處理,必要時可使用Web Worker開啓多線程

7. DOM渲染時間

這個時間就在於減小重排(頁面佈局變更)和重繪(頁面從新渲染元素樣式)

減小重排和重繪的手段

  • JS 中 CSS 屬性讀寫分離
  • 切換 class 或者 style.csstext 屬性來批量修改樣式
  • 將沒用的元素設爲不可見
  • 壓縮 Dom 的深度,多使用僞元素或者 box-shadow
  • 指定 img 標籤的大小
  • 使用獨立渲染層(觸發新的渲染層的方式或元素:Video元素,WebGL,Canvas,CSS3 3D,CSS濾鏡,z-index大於相鄰節點)
  • DOM 元素離線更新(使用appendChild和Document Fragment)

8. Load事件執行時間

這個時間的壓縮就是減小onload回調函數的耗時處理

不少人習慣將事件綁定放在onload事件中,實際上是能夠將其提早,放在尾部JS中直接執行或者DOMContentLoaded事件中,這時候DOM解析完成了,已經能夠獲取到DOM節點了

總結

本文從頁面加載的各個階段分析影響因素,簡單的給出了可優化的方向,具體實施還須要在工做中結合團隊現狀來進行

相關文章
相關標籤/搜索