網頁提速,你的小抄來了

Photo of author
Written By CMO

網頁下載,這是和生命在賽跑
網頁下載,這是和生命在賽跑

人人都知道網頁秒開有多重要,它不但能提高轉化率而且能增加SEO效果,不管是谷歌還是Google都把網頁載入速度歸為重要的排名因素,畢竟沒有一個搜索引擎會把用戶送去體驗超級差的頁面。 SEO中的鄙視鏈有很多種,比如國際化SEO鄙視本地SEO,白帽SEO鄙視黑帽SEO(當然黑帽是不屑的,偷著樂),還有技術SEO鄙視非技術SEO。HubSpot One本篇將為讀者從技術角度解讀頁面載入速度中的關鍵渲染路徑,關鍵渲染路徑決定了首屏展現速度。如果你缺乏前端開發的知識,那麼請記住結論。

TL;DR

  1. 減少關鍵渲染路徑中的阻塞資源是提升頁面性能加快網頁下載的重要方法。
  2. CSS資源要放在頁面頭部要權衡整站和單獨頁面,通過內嵌、合併、瘦身、共用緩存來進行優化。
  3. 外部JS文件要放在頁面末尾並應用async或defer屬性減少阻塞,也可以進行“懶加載”實現。
  4. DOMInteractive是谷歌常用的指標用於衡量可交互時間,優化方法同上。

關鍵渲染路徑的基本思路

除了保證優秀的Hosting(開啟HTTP/2與壓縮)和應用CDN和本地緩存,優化頁面的關鍵渲染路徑(Critical Rendering Path)也是提升網頁速度和用戶體驗的重要方法。而優化頁面的關鍵渲染路徑包含了三件事:

  1. 減少關鍵資源請求數
  2. 減少關鍵資源大小
  3. 縮短關鍵渲染路徑

第一點我們可以通過合併資源來實現,比如合併CSS文件,合併JS文件,合併一些小圖片,也可以使用矢量或Base64格式圖片,圖片懶加載(Lazy Load)等實現。第二點我們可以通過GZIP壓縮和圖片資源壓縮來實現。我們說關鍵的第三點。縮短關鍵渲染路徑體現在減少頁面阻塞資源上。

頁面加載的阻塞資源

我們必須了解網頁載入的整個過程,我們在前文講《Google Analytics跟踪網站頁面速度》中介紹過,見下圖:

頁面打開的整個過程
頁面打開的整個過程

瀏覽器會下載HTML文檔逐行來解析(Parse)。當讀到CSS和JS時整個過程便會被阻塞。

CSS的阻塞影響

瀏覽器讀到外部CSS時會異步地對外部CSS進行下載,然後逐步組合各個CSS樣式成為CSSOM。這裡CSS文件還可能導入其他的CSS文件。如果CSS沒處理完HTML這邊建的DOM就不會和CSSOM合併為Render Tree。

DOM和CSSOM結合為Render Tree
DOM和CSSOM結合為Render Tree

這就好比此時的DOM是未受精的雞蛋,孵不出小雞。這就會阻塞JS的運行,因為JS很可能需要CSS的標記。 CSS是無法Lazy Load的阻塞資源。我們可以通過合併CSS文件減少請求數,刪除不需要的樣式片段,以及把CSS寫到HTML文件內的inline方法來進行優化。由於我們需要有效的利用緩存,因此我們也需要在上面這些措施和緩存之間進行平衡。

JS的阻塞影響

有了JS的情況就複雜一些了。當瀏覽器解析到JS片段時(標籤為<script>),解析器就會停下來,這個JS片段後所有的資源都會等著。如果是外部JS那麼就先去下載,再去運行。運行完之前這個JS片段後所有的HTML都會被無視。

那你可能要問了,這片段上面的HTML建DOM Tree還沒建完呢?那是不是就必須乾等著JS跑完呢?不是這樣的,這裡有一個首次繪製(First Paint)的概念。

原來現代瀏覽器遇到這種情況會立刻拿已有的半截DOM Tree和已經完成的CSSOM進行首次繪製。這就是為什麼老人們總是說要把JS放在</body>前面。因為這樣這個半截DOM Tree就近似於完整的DOM Tree了。老人們還說要把CSS放在</head>前面,這是因為如果放在阻塞的JS之後就無法在首次繪製的時候被應用,解析器是無視JS片段之後的代碼的。首次繪製之前,我們看到的瀏覽器是個白屏。

為了專門應付這個情況瀏覽器和HTML也在進化。現在我們用的大多數瀏覽器都支持async和defer兩個屬性。這兩個屬性的共同點是不會阻塞解析器繼續解析JS片段下的HTML,區別是async在腳本下載後(如果CSSOM已完成)便會阻塞解析器開始執行,而defer要等Render Tree完成了才會執行。

async與defer的作用
async與defer的作用

因此我們優化JS的方式就是將對首屏內容起影響的JS內容作為inline放到HTML中,而將其他的JS文件設置為async或者defer,最後為了滿足古董瀏覽器把它們放在</body>之前

這裡我們要注意的是async和defer雖然能讓外部JS變成非阻塞資源,讓Google PageSpeed Insights不再圈出它們,但是並不能讓JS資源真正在頁面完成後才載入。如果你想要“Lazy Load”一下JS資源可以採用Patrick Sexton提供的方法(建議去讀一下他的頁面性能指南)。這樣只有在這段代碼執行以後才會加載defer.js,而這段代碼一定要全部頁面Loaded以後才會加載並運行defer.js。

< script type = "text/javascript" > function downloadJSAtOnload ( ) { var element = document. createElement ( "script" ) ;
element. src = "defer.js" ;
document. body . appendChild ( element ) ; } if ( window. addEventListener )
window. addEventListener ( "load" , downloadJSAtOnload , false ) ; else if ( window. attachEvent )
window. attachEvent ( "onload" , downloadJSAtOnload ) ; else window. onload = downloadJSAtOnload ; </ script >

說到這裡,我們解釋了阻塞資源和如何避免阻塞資源帶來的影響。而且在上面代碼中,我們提到了load這個這個瀏覽器event。我們在本篇最後再談談另外兩個與穀歌判斷網頁速度相關的事件——DOMContentLoaded和DOMInteractive

DOMContentLoaded與DOMInteractive

DOMContentLoaded代表著DOM和CSSOM都已經就緒,所有阻塞的JS資源都已經執行完畢。 “吉時已到”!如果本身就不存在阻塞的JS資源,那麼在DOMInteractive事件之後就會立即發生DOMContentLoaded事件。

DOMContentLoaded在Chrome DevTools中
DOMContentLoaded在Chrome DevTools中

DOMContentLoaded可以用於我們的KPI,這個時刻越早代表了我們越早能夠有Render Tree,我們遇到的阻塞越少

DOMInteractive事件也常常被用於衡量頁面性能。它標誌了DOM已經構建完畢,如果這個時候CSSOM也構建完畢且沒有阻塞JS資源,那麼將馬上發生DOMContentLoaded事件。相對於DOMContentLoaded,DOMInteractive事件主要被阻塞的JS資源影響比較這兩者的區別可以給我一些優化啟示。

Google Analytics用DOMInteractive作為衡量網頁速度的指標之一,但是下面Google Analytics文檔的描述是錯誤的

Avg. Document Interactive Time : The average time (in seconds) that the browser takes to parse the document (DOMInteractive), including the network time from the user’s location to your server. At this time, the user can interact with the Document Object Model even though it is not fully loaded .

恰恰相反,我們說了DOMInteractive代表整個HTML已經解析完了,DOM已經構建。正是由於一些阻塞資源的影響,DOMInteractive事件之前用戶可能已經因為首次繪製看到了部分內容(JS阻塞資源影響);而且即便DOMInteractive事件已經發生,用戶也可能因為CSSOM未完成看到的還是白屏(CSS阻塞資源影響)。因此只能說DOMInteractive只是一個折衷的指標。至於Google的PageSpeed Insights中的可交互耗時(TTI-Time to Interactive)非常有意思的是它既沒有用First Paint也沒有用DOMInteractive來進行衡量,而是用了First Contentful Paint。原因是前兩個指標不能衡量此時用戶是否能真正開始交互,雖然願望很美好:

谷歌對可交互時間TTI的定義
谷歌對可交互時間TTI的定義

要提高TTI的評分還是要注意消除阻塞資源的影響。

總結

本篇我們圍繞著關鍵渲染路徑介紹了優化頁面性能的方法,這有助於你在GA中和PageSpeed Insights中取得好成績。但是你的最終目標是為用戶提供優秀的交互體驗,請牢記這個目標。筆者只是一個熱愛前端技術的SEO或者熱愛前端技術的網頁分析師,理解和行文難免有所疏漏,希望不嗇指出。