上個月,我寫了一篇文章介紹什麼是「關鍵渲染路徑」,其實目的是爲了給這篇文章作一個鋪墊,本文將談談如何優化關鍵渲染路徑(本文將假設您已經閱讀過《關鍵渲染路徑》這篇文章或已經懂得了什麼是「關鍵渲染路徑」)。php
優化關鍵渲染路徑能夠提高網頁的渲染速度,從而獲得一個更好的用戶體驗。css
優化關鍵渲染路徑有不少種方法與狀況,不一樣狀況下優化方式也各不相同,初步看起來這些優化方法五花八門,知識很是的零散。html
但在這些看似零散的知識中,咱們會發現一些規律,將這些規律總結起來後,能夠得出一個結論:到目前爲止,只有三種因素能夠影響關鍵渲染路徑的耗時。而全部的優化方式,都是在儘量的針對這三種因素進行優化。git
這三種因素分別是:github
切記,很是重要,全部優化關鍵渲染路徑的方法,都是在優化以上三種因素。由於只有這三種因素能夠影響關鍵渲染路徑。瀏覽器
關鍵資源指的是那些能夠阻塞頁面首次渲染的資源。例如JavaScript、CSS都是能夠阻塞關鍵渲染路徑的資源,這些資源就屬於「關鍵資源」。關鍵資源的數量越少,瀏覽器處理渲染的工做量就越少,同時CPU及其餘資源的佔用也越少。緩存
關鍵路徑中的每一步耗時越長,因爲阻塞會致使渲染路徑的總體耗時變長。關鍵路徑的長度指的是關鍵渲染路徑的總耗時。關鍵渲染路徑的長度會受到不少因素的影響。例如:關鍵資源的網絡狀況、關鍵資源的數量、關鍵資源的字節大小、關鍵資源的依賴關係等。網絡
關鍵字節的數量指的是關鍵資源的字節大小,瀏覽器要下載的資源字節越小,則下載速度與處理資源的速度都會更快。一般不少優化方法都是針對關鍵字節的數量進行優化。例如:壓縮。app
在關鍵渲染路徑中,構建渲染樹(Render Tree)的第一步是構建DOM,因此咱們先討論如何讓構建DOM的速度變得更快。dom
HTML文件的尺寸應該儘量的小,目的是爲了讓客戶端儘量早的接收到完整的HTML。一般HTML中有不少冗餘的字符,例如:JS註釋、CSS註釋、HTML註釋、空格、換行。更糟糕的狀況是我見過不少生產環境中的HTML裏面包含了不少廢棄代碼,這多是由於隨着時間的推移,項目愈來愈大,因爲種種緣由從歷史遺留下來的問題,不過無論怎麼說,這都是很糟糕的。對於生產環境的HTML來講,應該刪除一切無用的代碼,儘量保證HTML文件精簡。
總結起來有三種方式能夠優化HTML:縮小文件的尺寸(Minify)、使用gzip壓縮(Compress)、使用緩存(HTTP Cache)。
縮小文件的尺寸(Minify)會刪除註釋、空格與換行等無用的文本。
本質上,優化DOM實際上是在儘量的減少關鍵路徑的長度與關鍵字節的數量。
與優化DOM相似,CSS文件也須要讓文件儘量的小,或者說全部文本資源都須要。CSS文件應該刪除未使用的樣式、縮小文件的尺寸(Minify)、使用gzip壓縮(Compress)、使用緩存(HTTP Cache)。
除了上面提到的優化策略,CSS還有一個能夠影響性能的因素是:CSS會阻塞關鍵渲染路徑。
CSS是關鍵資源,它會阻塞關鍵渲染路徑也並不奇怪,但一般並非全部的CSS資源都那麼的『關鍵』。
舉個例子:一些響應式CSS只在屏幕寬度符合條件時纔會生效,還有一些CSS只在打印頁面時才生效。這些CSS在不符合條件時,是不會生效的,因此咱們爲何要讓瀏覽器等待咱們並不須要的CSS資源呢?
針對這種狀況,咱們應該讓這些非關鍵的CSS資源不阻塞渲染。
實現這一目的很是簡單,咱們只須要將不阻塞渲染的CSS移動到單獨的文件裏。例如咱們將打印相關的CSS移動到print.css
,而後咱們在HTML中引入CSS時,添加媒體查詢屬性print
,代碼以下:
<link href="print.css" rel="stylesheet" media="print">
複製代碼
上面代碼添加了media="print"
屬性,因此上面CSS資源僅用於打印。添加了媒體查詢屬性後,瀏覽器依然會下載該資源,但若是條件不符合,那麼它就再也不阻塞渲染,也就是變成了非阻塞的CSS。
咱們能夠寫個DEMO測試一下:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="https://static.xx.fbcdn.net/rsrc.php/v3/y6/l/1,cross/9Ia-Y9BtgQu.css">
</head>
<body>
Hello
</body>
</html>
複製代碼
上面代碼使用Chrome開發者工具的性能面板捕獲後的性能圖以下:
從上圖中的首次繪製(First Paint)時間是在1200ms的位置,能夠看到這個時間是瀏覽器加載CSS完畢後,並且能夠看到Network欄中CSS顯示Highest
表明高優先級。
添加了媒體查詢語句後,捕獲出來的性能圖以下:
首次繪製時間在不到100ms的位置,和domcontentloaded事件差很少的時間觸發。同時CSS資源變成了Lowest
,表示低優先級。
能夠看到,瀏覽器依然會下載該CSS資源,但它再也不阻塞渲染。
上面提供的方法是針對那些不須要生效的CSS資源,若是CSS資源須要在當前頁面生效,只是不須要在首屏渲染時生效,那麼爲了更快的首屏渲染速度,咱們能夠將這些CSS也設置成非關鍵資源。只是咱們須要一些比較hack的方式來實現這個需求:
<link href="style.css" rel="stylesheet" media="print" onload="this.media='all'">
複製代碼
上面代碼先把媒體查詢屬性設置成print
,將這個資源設置成非阻塞的資源。而後等這個資源加載完畢後再將媒體查詢屬性設置成all
讓它當即對當前頁面生效。
經過這樣的方式,咱們既可讓這個資源不阻塞關鍵渲染路徑,還可讓它加載完畢後對當前頁面生效。
相似的方案有不少,代碼以下:
<link rel="preload" href="style.css" as="style" onload="this.rel='stylesheet'">
<link rel="alternate stylesheet" href="style.css" onload="this.rel='stylesheet'">
複製代碼
上面兩種方式都能實現一樣的效果。
關於CSS的加載有這麼多門道,到底怎樣纔是最佳實踐?答案是:Critical CSS。
Critical CSS的意思是:把首屏渲染須要使用的CSS經過style標籤內嵌到head標籤中,其他CSS資源使用異步的方式非阻塞加載。
CSS資源在構建渲染樹時,會阻塞JavaScript,因此咱們應該保證全部與首屏渲染無關的CSS資源都應該被標記爲非關鍵資源。
因此Critical CSS從兩個方面解決了性能問題:
你們應該都知道要避免使用@import
加載CSS,實際工做中咱們也不會這樣去加載CSS,但這究竟是爲何呢?
這是由於使用@import
加載CSS會增長額外的關鍵路徑長度。舉個例子:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="http://127.0.0.1:8887/style.css">
<link rel="stylesheet" href="https://lib.baomitu.com/CSS-Mint/2.0.6/css-mint.min.css">
</head>
<body>
<div class="cm-alert">Default alert</div>
</body>
</html>
複製代碼
上面這段代碼使用link
標籤加載了兩個CSS資源。這兩個CSS資源是並行下載的。咱們使用Chrome開發者工具的Performance面板捕獲出的結果以下圖所示:
從圖中用紅色方框圈出來的位置能夠看出兩個CSS是並行加載的,首次繪製時間取決於CSS加載時間較長的資源加載時間。
如今咱們改成使用@import
加載資源,代碼以下:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="http://127.0.0.1:8887/style.css">
</head>
<body>
<div class="cm-alert">Default alert</div>
</body>
</html>
複製代碼
/* style.css */
@import url('https://lib.baomitu.com/CSS-Mint/2.0.6/css-mint.min.css');
body{background:red;}
複製代碼
代碼中使用link標籤加載一個CSS,而後在CSS文件中使用@import
加載另外一個CSS。使用Chrome開發者工具再次捕獲出的結果以下圖所示:
能夠看到兩個CSS變成了串行加載,前一個CSS加載完後再去下載使用@import
導入的CSS資源。這無疑會致使加載資源的總時間變長。從上圖能夠看出,首次繪製時間等於兩個CSS資源加載時間的總和。
因此避免使用@import
是爲了下降關鍵路徑的長度。
全部文本資源都應該讓文件儘量的小,JavaScript也不例外,它也須要刪除未使用的代碼、縮小文件的尺寸(Minify)、使用gzip壓縮(Compress)、使用緩存(HTTP Cache)。
與CSS資源類似,JavaScript資源也是關鍵資源,JavaScript資源會阻塞DOM的構建。而且JavaScript會被CSS文件所阻塞。爲了不阻塞,能夠爲script
標籤添加async
屬性。
咱們舉個例子:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="https://static.xx.fbcdn.net/rsrc.php/v3/y6/l/1,cross/9Ia-Y9BtgQu.css">
</head>
<body>
<p class='_159h'>aa</p>
<script src="http://qiniu.bkt.demos.so/static/js/app.53df42d5b7a0dbf52386.js"></script>
</body>
</html>
複製代碼
上面這段代碼,分別加載了CSS資源和JavaScript資源,咱們使用Chrome開發者工具的Performance面板捕獲出的結果以下圖所示:
從捕獲出的結果能夠看到,JS資源加載完畢後,須要等待CSS資源加載完並構建出CSSOM以後纔會執行JS,而且JS會將DOM阻塞,因此最終domcontentloaded
事件在350ms與400ms之間觸發。
咱們將script標籤添加async
屬性:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="https://static.xx.fbcdn.net/rsrc.php/v3/y6/l/1,cross/9Ia-Y9BtgQu.css">
</head>
<body>
<p class='_159h'>aa</p>
<script async src="http://qiniu.bkt.demos.so/static/js/app.53df42d5b7a0dbf52386.js"></script>
</body>
</html>
複製代碼
使用Chrome開發者工具捕獲出的結果以下圖所示:
從圖中能夠看到,JS加載完後再也不須要等待CSS資源,而且也再也不阻塞DOM的構建,最終domcontentloaded
事件在50ms與100ms之間觸發。與以前相比,domcontentloaded
事件觸發時間提早了300ms。
能夠看到,在關鍵渲染路徑中優化JavaScript,目的是爲了減小關鍵資源的數量。
該篇文章詳細介紹瞭如何優化關鍵渲染路徑。
關鍵渲染路徑是瀏覽器將HTML,CSS,JavaScript轉換爲屏幕上所呈現的實際像素的具體步驟。而優化關鍵渲染路徑能夠提升網頁的呈現速度,也就是首屏渲染優化。
你會發現,咱們介紹的內容都是如何優化DOM,CSSOM以及JavaScript,由於一般在關鍵渲染路徑中,這些步驟的性能最差。這些步驟是致使首屏渲染速度慢的主要緣由。