一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?

接灰的電子產品發表於2017-01-18

譯者注:昨天一篇工作僅一年的前端工程師面試幾個大廠的文章 (1月前端面試記) 在掘金火爆起來。一方面大家覺得作者太厲害了,工作近一年,能力竟然這麼強(大叔我表示慚愧),另一方面幾個微信面試題引起很多討論。其中我比較感興趣的一個題目是關於JavaScript和CSS阻塞DOM的。老實話講,我以前也沒太關注過,與其哀嘆自己研究的不夠深入,不如我們去學習彌補一下吧。這篇文章是我今天讀到的,感覺不錯,翻譯一下給大家共享。

原文地址:www.keycdn.com/blog/blocki…
原文作者:BRIAN JACKSON


當我們談到web效能或者優化頁面級別的速度時,非常重要的一點是要理解HTML和一個頁面是如何在瀏覽器中構造的,這樣你才能找到由於渲染阻塞導致的頁面載入延遲。在這篇文章中,我們會深入瞭解是 什麼阻塞了DOM 以及你應該怎樣避免這種情況。

什麼是DOM?

DOM是Document Object Model(文件物件模型)的縮寫。它是為HTML和XML定義的一個程式設計介面,提供了文件的結構化表示(節點樹狀結構),同時也規定了使用指令碼程式語言(例如JavaScript)應該如何訪問以及操作DOM。這樣一個節點樹狀結構是由不同的元素、父節點、子節點、兄弟節點等構成,它們彼此都有層級化的關係。下圖是一個HTML DOM的例子:

一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?
HTML DOM

用人話描述DOM

簡單的講,當你使用一個類似Chrome開發者工具的東東時,你可以看到一個視覺化的DOM。你的HTML並不是DOM,但Chrome開發者工具為你展現了一個經過HTML或JavaScript加工之後的DOM。所以你可以把DOM理解成解析後的HTML。

一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?
Chrome開發者工具中體現的DOM

什麼在阻塞DOM?

當我們分析頁面速度時,我們總要考慮什麼阻塞了DOM導致我們的頁面載入出現延遲。這些阻塞因素我們可以叫做 阻塞渲染的資源 ,例如 HTML、CSS(也包括web font)和 JavaScript。

要檢視什麼阻塞了DOM的最簡單的方法之一就是使用 Chrome開發者工具 (Chrome DevTools) 和Google的 PageSpeed Insight。在下面的例子中,我們使用了最新的Chrome開發者工具 (可以通過 Chrome Canary 獲得)。

  1. 在Chrome中啟動開發者工具
    • Windows:F12 或者 Ctrl + Shift + I
    • Mac: Cmd + Opt + I
  2. 切換到 Network (網路)皮膚,重新整理頁面( Win: Ctrl + R, Mac: Cmd + R
  3. 現在你會看到一個載入時間瀑布圖。這裡有兩個值得我們關注的東東:第一個是 DOMContentLoaded 是384ms(譯者注:原文如此,看圖的話應該是281ms),第二個就是瀑布圖中的在藍線之前的綠色部分(譯者注:原圖有點問題,藍線看起來是紫色的)

我們知道CSS和JavaScript都是阻塞渲染的資源,它們都會在藍色的DOMContent之前載入。請注意,影像是不會阻塞渲染的 ,所以如果有影像落在藍線之前或之上你可以放心的忽略掉,當然優化影像也是很重要的一項工作。在這個例子裡面,我們可以看到 style.cssjquery.min.js 都是阻塞渲染的資源。

一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?
Chrome開發者工具的網路皮膚中的載入時間瀑布圖

你同樣可以通過 Google PageSpeed Insightsdevelopers.google.com/speed/pages… 工具來驗證我們上面的結論。下圖中顯示這兩個檔案的確是阻塞渲染的。

一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?
Google PageSpeed Insights

我們下面要學習的是如何 通過優化關鍵渲染路徑來避免CSS和JavaScript阻塞DOM 。儘管HTML也算是一個阻塞渲染的資源(譯者注:記住HTML不是DOM),但DOM是可以增量構建的(譯者注:所以優化的是CSS和JavaScript,而不是HTML)。

注意,我們無需追求在 Google PageSpeed Insights100/100。例如,如果你連結引用了Google的web字型,那麼無論你做什麼,這個外部的 fonts.googleapis.com 樣式都始終會是一個阻塞渲染的資源。重要的是當你在處理有著10+個阻塞渲染資源的一個大型站點時,要理解清楚什麼導致了延遲,有什麼樣的策略可以使這些資源可以更有效率的載入。

CSS

非渲染阻塞的CSS

如果你追求一個完全沒有阻塞的CSS,那麼你的唯一選項就是:在HTML中內聯嵌入你的CSS。你可以把需要初始渲染的CSS,一般來講就是第一屏的樣式,直接放在 HEAD 裡面的 <style></style> 中,然後剩下的CSS放在 </body> 之前。這樣做可以完全避免CSS阻塞渲染。

有幾個可以輔助你完成內聯樣式嵌入的自動化外掛

你當然也可以使用JavaScript來載入CSS,但是這樣做會導致頁面在載入結束時重繪,因此這個選項對於網站訪問者來說不一定會很理想。

在Chrome開發者工具中可以看到,我們做完內聯樣式優化之後的版本中 DOMContentLoaded 減少到了 278ms

一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?
內聯優化後的版本

現在我們再去 Google PageSpeed Insights 測試,會發現CSS已不是阻塞渲染的資源了。

一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?
內聯優化後的測試

當然這很不錯,但一切取決於你的站點實際情況。大多數站點並不想內聯嵌入所有的CSS,因為CSS的內容多少直接且顯著的影響了頁面下載的大小。對於小型站點或者就是個 Landing Page ,這種情況下內聯嵌入CSS可以是一個不錯的選項,如果你真的想完全避免CSS阻塞渲染的話。

我們的CSS建議

即使在我們自己的KeyCDN主頁上,我們也有一個阻塞渲染的CSS。但是,我們做了其他一些事情來優化CSS的載入時間,下面是我們的建議:

  1. 正確的呼叫你的CSS檔案 (譯者注:原文如此,感覺應該是位置或時機?)
  2. 使用 media queries (媒體查詢) 來標記某些CSS為非阻塞資源 (譯者注: 比如 <link href="other.css" rel="stylesheet" media="(min-width: 40em)"> 這樣可以在其他螢幕尺寸載入時就不用載入這個css了)
  3. 減少CSS的數量(儘可能放到一個CSS檔案中)
  4. Minify CSS檔案(刪除多餘的空格、字元、註釋等)
  5. 儘可能的減少樣式數量(譯者注:和第三條不同,是減少樣式數量,不是檔案數量)

一些用於最小化(Minify)CSS的工具

JavaScript

非渲染阻塞的JavaScript

有一些關於JavaScript的最佳實踐需要牢記在心:

  1. 把指令碼放在頁面尾部 </body> 之前的位置
  2. 使用async或defer指令來避免阻塞渲染

非同步載入JavaScript

async 允許指令碼在後臺下載,因此是無阻塞的。但是當下載完成的時刻,渲染又會阻塞了,這是因為指令碼執行了。當指令碼執行完畢,渲染又恢復了。

 <script async src="foobar.js"></script>複製程式碼

延遲載入JavaScript

defer 指令做的事情和 async 基本一樣,區別點在於 defer 是嚴格要求指令碼的執行順序必須和在HTML中標記的順序一樣。所以說,可能存在一種情況,當一些指令碼已經下載完畢,這些指令碼不會立即執行,它們會等待其他指令碼下載完成,因為那些指令碼在HTML中出現在它們之前。

Patrick Sexton寫了一篇非常好的博文: 如何延遲載入JavaScript

除去上面兩條,我們對於JavaScript的另外3條建議是:

  1. 減少JavaScript的數量(儘量整合成一個JS檔案)
  2. Minify(最小化)JavaScript
  3. 如果JavaScript很小的話,可以內聯嵌入

用於最小化JavaScript的自動化任務外掛

  • Grunt:grunt-contrib-uglify
  • Gulp:gulp-uglify

    通過把我們的JavaScript移動到頁面尾部,以及使用 async 指令之後,我們把 DOMContentLoaded 顯著的減少到了 144ms。我們可以看到 jquery.min.js 檔案現在出現在了DOM的藍線之後了。

一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?
JavaScript優化後在Chrome開發者工具中的表現

那麼在 Google PageSpeed Insights 中同樣的,由於我們已經非同步載入了JavaScript,所以這一項的扣分不存在了,我們達成了 100/100

一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?
達成100分

Web Fonts

Web Fonts(Web字型)也被視為一種阻塞渲染的資源,因為它們是通過CSS載入的。你有兩個選擇:阻塞渲染或者延遲重繪(這種情況你需要處理 FOUT)。舉個例子,在Chrome(36以上版本),Opera(23以上版本)和Firefox中有一個 three-second timeout,在超時後,fall-back字型會被使用。

同樣的我們有幾個關於載入字型和優化關鍵渲染路徑的小建議:

  1. 使用Web Font載入器或者字型載入API
  2. 使用內聯嵌入優化字型載入
  3. 使用例如localStorage等儲存方法

關於更多深入的載入Web Fonts、如何避免渲染阻塞以及FOUT/FOIT等可以檢視這篇博文:
analyzing web font performance

小總結

我們希望到這裡你可以對阻塞DOM、DOM樹是如何構建的,為何會被CSS和JavaScript阻塞等問題有了一些瞭解。請再次記住不要追求 Google PageSpeed Insights100/100,重要的是理解你的渲染阻塞資源是如何阻塞DOM的以及你會怎樣正確的優化使得頁面的載入變快。


本文對你有幫助?歡迎掃碼加入前端學習小組微信群:

一個微信面試題引發的血案 --[譯] 什麼阻塞了 DOM?

相關文章