博客 / 詳情

返回

網頁加載性能優化方法研究

網頁的加載性能是影響用户體驗的最重要因素,頁面加載時間過長,極有可能會令用户直接關閉網頁,即使網頁本身的流程和UI等方面優化得再出色,也不會有任何價值。本文將以優化網頁加載性能的角度出發,介紹網頁渲染的過程以及各類資源阻塞網頁渲染的情況,並給出優化的方向
(本文以Chrome為主瀏覽器進行討論,其他瀏覽器可能會存在細微不同的情況,不在本文討論的範圍)

網頁的渲染過程

五個步驟

想要知道如何優化網頁加載性能,首先得清楚瀏覽器加載一個網頁經過了哪些步驟,才能明白應該從哪些方面着手優化。一般來説,瀏覽器從服務器端獲取到HTML文件後就開始了加載過程,加載網頁的過程包括如下五個步驟:

  • HTML代碼轉化成DOM,並並行地獲取外部資源(包括HTML中出現的JS、CSS、圖片文件)
  • CSS文件下載完成,CSS代碼轉化成CSSOM(CSS Object Model)
  • 結合DOM和CSSOM,生成一棵渲染樹(包含每個節點的視覺信息)
  • 生成佈局(Layout):將所有渲染樹的所有節點進行平面合成,該步驟可以計算出元素在網頁中的位置
  • 繪製(Paint):將網頁內容繪製在屏幕上

在加載過程中,前三步的耗時主要在於外部資源獲取上,只有CSS資源下載完成才能完成第二第三步,生成渲染樹;而後兩步Layout和Paint較為耗時,且在網頁渲染過程中會執行多次,其中首次Paint的耗時可以視為頁面從空白到有內容的時間,減小這部分用時可以給用户以網頁加載快的錯覺

瀏覽器實踐

瀏覽器具備一些工具可以記錄頁面渲染的過程,進而幫助我們分析哪些方面的性能還有提升的空間。
以最簡單的HTML結構來熟悉一下Chrome中的Performance工具(之前的名字是timeline)。

首先我們打開控制枱,切換到Performance工具,在頁面加載前點擊左上角的圓點,開始錄製,頁面加載完再次點擊,停止錄製,等數據分析完成切換到“Event Log”就可以開始查看渲染過程了。
圖片描述

我們先以最簡單的結構進行分析,如加載下方index.html文件:

<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>測試瀏覽器渲染</title>
  <link rel="stylesheet" href="./assets/style.css">
</head>
<body>
  <h1>koa by html file</h1>
  <script src="./assets/script.js"></script>
  <img width="300" src="./assets/image.png">
</body>
</html>

分析這個網頁加載情況,如下截圖所示:
圖片描述

(上圖為渲染五個步驟中的第一步,接收html文件解析並並行請求外部資源)

圖片描述

(上圖為2, 3和4, 5步,接收到CSS文件後構建渲染樹,進行Layout和首次的Paint)

圖片描述

(上圖為Load事件之後的Paint)

接着我們修改一下node層代碼,令CSS文件返回延遲5s,會得到如下“Event Log”

圖片描述

(css文件延遲5s後,會發現首次Layout和Paint都延遲到5s之後)

為了研究JS文件對首次Paint的影響,我們令JS文件返回延遲5s,CSS和圖片都即時返回,會得到如下“Event Log”

圖片描述

從上面的分析可以看出:

  • 外部資源是在開頭並行獲取的,並非HTML解析到引用行再發請求的;
  • CSS文件加載的速度是影響首次Paint時間的因素,當CSS文件下載完成後才能進行CSSOM和渲染樹的構建,然後才是Layout和Paint過程;
  • JS文件加載的速度不影響首次Paint時間,即使JS文件本身有修改DOM節點的操作,也只是在JS文件加載完成後再進行一次Layout和Paint;

各類資源阻塞網頁加載的情況

阻塞網頁加載的情況,可以分為兩種:一種是阻塞HTML解析,即阻塞Event Log中的parse HTML步驟,令HTML結構停止解析;另一種是阻塞渲染,即阻塞Event Log中的Paint步驟,令已經解析好的HTML結構也無法顯示到瀏覽器上。

CSS資源

CSS樣式無法影響DOM結構,但能改變頁面中元素呈現的模樣,所以CSS資源可以阻塞渲染,但不會阻塞HTML解析。

為了研究CSS是否會阻塞渲染和解析,我們依然使用之前的index.html文件,只將style.css延遲5s返回,得到如下實驗結果:

圖片描述

(解析完HTML之後,就並行發JS、CSS和圖片請求,JS和圖片先接收完成,但沒有觸發首次Paint渲染,而是延遲到css文件接收完成,CSSOM和渲染樹構建完成才觸發了首次Paint,因此CSS資源會阻塞渲染)

相同情況下,頁面的展示和HTML結構如何呢?請看下方動圖:

圖片描述

(在CSS文件接收完成之前,雖然HTML結構已經解析完成,即Elements中HTML結構已經被解析出來,但頁面是一片空白,説明CSS資源不會阻塞解析,但會阻塞渲染

JS資源

JS腳本會影響DOM結構,也能改變頁面中元素呈現的模樣,所以JS資源既能阻塞渲染又能阻塞HTML解析。

為了研究JS是否會阻塞渲染和解析,我們依然使用index.html文件,只將script.js延遲5s返回,得到如下實驗結果:

圖片描述

(並行發送請求,CSS和圖片資源先加載完,觸發了首次Paint渲染,此時JS文件還沒開始下載,所以JS文件並不會影響頁面的首次渲染)

圖片描述

(延遲了5s後,開始接收JS文件,完成後,再次Paint渲染)

相同情況下,頁面的展示和HTML結構,請看下方動圖:

圖片描述

(JS開始下載之前,CSS資源已經下載完成,頁面也解析並渲染出一部分內容,這部分是JS文件由script標籤引入之前的內容,而script標籤之後的圖片則需等到JS文件加載完成後才進行解析並渲染出來,因此JS資源會阻塞之後的HTML結構解析和渲染

圖片資源

圖片資源無法對其他DOM節點進行操作,所以不會阻塞渲染和HTML解析。

為了研究圖片資源是否會阻塞渲染和解析,我們依然使用index.html文件,只將png文件延遲5s返回,得到如下實驗結果:

圖片描述

(並行發送請求,CSS和JS文件都加載後,完成HTML解析和首次Paint渲染)

圖片描述

(當圖片文件加載完成後,再次渲染)

相同情況下,頁面的展示和HTML結構,請看下方動圖:

圖片描述

(圖片加載之前,頁面的HTML結構已經解析完成,並且畫面也出現在屏幕上,説明圖片資源不會阻塞HTML的解析和畫面渲染

針對首屏的優化建議

從上面的實驗可以看出,三種資源中只有CSS文件會影響首次Paint的時間。無論是延遲JS還是圖片,在CSS文件加載完成後,都能先渲染出已經解析好的HTML內容。由於在首次Paint之前,頁面是一片空白,所以首次Paint的時間也就相當於首屏時間。

針對首次Paint時間的優化,我們可以從以下角度進行考慮:

  • CSS會影響首次Paint的時間,應該儘快加載,因此將其放在head標籤處以保障能在解析完HTML後發出請求;
  • JS不會影響首屏的時間,但由於JS會阻塞之後的頁面內容的解析與渲染,要避免JS在首屏位置出現,一般將它們放在body的最後,另外,JS會影響整體頁面加載的性能,可以使用defer和async轉成非阻塞模式(見參考文獻[6]);
  • 圖片不影響首屏時間,但會影響Load事件的時機(沒有找到合適的參考文獻,待我另起一篇文章補充);

參考文獻

  1. 《js一定要放在底部嗎?》https://segmentfault.com/a/11...
  2. 《聊聊瀏覽器的渲染機制》https://www.jianshu.com/p/c90...
  3. 《網頁性能管理詳解》http://www.ruanyifeng.com/blo...
  4. 《瀏覽器渲染過程與性能優化》https://sylvanassun.github.io...
  5. 《How browsers work》http://taligarsiel.com/Projec...
  6. 《JavaScript阻塞剖析與改善》http://www.cnblogs.com/giggle...
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.