The tutor of HTML rendering process

轉載自http://www.cnblogs.com/yuezk/archive/2013/01/11/2855698.html javascript

瞭解html頁面的渲染過程

最近在學習前端的性能優化,有必要了解一下頁面的渲染流程,以便對症下藥,找出性能的瓶頸所在。如下是我看到的一些東西,分享給你們。 css

參考:Understanding the renderer html

頁面的渲染有如下特色: 前端

  • 單線程事件輪詢
  • 定義明確、連續、操做有序(HTML5)
  • 分詞和構建DOM樹
  • 請求資源並預加載
  • 構建渲染樹並繪製頁面

具體來講: java

當咱們從網絡上獲得HTML的相應字節時,DOM樹就開始構建了。由瀏覽器更新UI的線程負責。當遇到如下狀況時,DOM樹的構建會被阻塞: 瀏覽器

  • HTML的響應流被阻塞在了網絡中
  • 有未加載完的腳本
  • 遇到了script節點,可是此時還有未加載完的樣式文件

渲染樹構建自DOM樹,而且會被樣式文件阻塞。 緩存

因爲是基於單線程的事件輪詢,即便沒有腳本和樣式的阻塞,當這些腳本或樣式被解析、執行而且應用的時候,也會阻塞頁面的渲染。 性能優化

一些不會阻塞頁面渲染的狀況: 網絡

  • 定義的defer屬性和async屬性的
  • 沒有匹配的媒體類型的樣式文件
  • 沒有經過解析器插入script節點或樣式節點

下面,經過一個例子來講明一下(完整的代碼): app

複製代碼
 1 <html>  2 <body>  3   <link rel="stylesheet" href="example.css">  4   <div>Hi there!</div>  5   <script>  6     document.write('<script src="other.js"></scr' + 'ipt>');  7   </script>  8   <div>Hi again!</div>  9   <script src="last.js"></script> 10 </body> 11 </html>
複製代碼

代碼很容易看明白,若是放在瀏覽器中打開會當即顯示出想要的頁面。下面,讓咱們用慢鏡頭回放的方式來看看它到底是怎麼渲染的。

1 <html> 2 <body> 3   <link rel="stylesheet" href="example.css"> 4 <div>Hi there!</div> 5 <script>...

首先,解析器遇到了example.css,並將它從網絡中下載下來。下載樣式表的過程是耗時的,可是解析器並無被阻塞,繼續往下解析。接下來,解析器遇到script標籤,可是因爲樣式文件沒有加載下來,阻塞了該腳本的執行。解析器被阻塞住,不能繼續往下解析。

渲染樹也會被樣式文件阻塞,因此這時候沒有瀏覽器不會去渲染頁面,換句話說,若是example.css文件下載不下來,Hi there! 是顯示不出來的。

接下來,繼續。。。

複製代碼
<html> <body>   <link rel="stylesheet" href="example.css"> <div>Hi there!</div> <script>   document.write('<script src="other.js"></scr' + 'ipt>'); </script>
複製代碼

一旦example.css文件加載完成,渲染樹也就被構建好了。

內聯的腳本執行完以後,解析器就會當即被other.js阻塞住。一旦解析器被阻塞,瀏覽器就會收到繪製請求,"Hi there!"也就顯示在了頁面上。

當other.js加載完成以後,解析器繼續向下解析。。。

複製代碼
1 <html> 2 <body> 3 <link rel="stylesheet" href="example.css"> 4   <div>Hi there!</div> 5   <script> 6     document.write('<script src="other.js"></scr' + 'ipt>'); 7   </script> 8   <div>Hi again!</div> 9   <script src="last.js"></script>
複製代碼

解析器遇到last.js以後會被阻塞,而後瀏覽器收到了另外一個繪製請求,"Hi again!"就顯示在了頁面上。最後last.js會被加載,而且會被執行。

可是,爲了減緩渲染被阻塞的狀況,現代的瀏覽器都使用了猜想預加載(speculative loading)。

在上面這種狀況下,腳本和樣式文件會嚴重阻塞頁面的渲染。猜想預加載的目的就是減小這種阻塞時間。當渲染被阻塞的時候,它會作如下一些事:

  • 輕量級的HTML(或CSS)掃描器(scanner)繼續在文檔中掃描
  • 查找那些未來可能可以用到的資源文件的url
  • 在渲染器使用它們以前將其下載下來

可是,猜想預加載不能發現經過javascript腳原本加載的資源文件(如,document.write())。

注:全部的「現代」瀏覽器都支持這種方式。

回過來再看上面的例子,經過猜想預加載這種方式是怎麼工做的。

1 <html> 2 <body> 3   <link rel="stylesheet" href="example.css"> 4   <div>Hi there!</div> 5   <script>...

解析器返現了example.css,並從網絡獲取,解析器沒有被阻塞,繼續解析,當遇到了內聯的script節點時,被阻塞住,因爲樣式文件沒有 加載完成,阻塞了腳本的執行。渲染樹一樣也被樣式文件阻塞住,因此瀏覽器沒有收到渲染請求,看不到任何東西。到目前爲止,和剛纔提到的那種方式是同樣的。 可是接下來就由變化了。

預加載器(Speculative loader)繼續「閱讀」文檔,發現了last.js並視圖加載它。接下來:

複製代碼
1 <html> 2 <body> 3   <link rel="stylesheet" href="example.css"> 4   <div>Hi there!</div> 5   <script> 6     document.write('<script src="other.js"></scr' + 'ipt>'); 7   </script>
複製代碼

一旦example.css文件加載完成,渲染樹也就完成了構建,內聯的腳本也能夠執行,以後解析器又被other.js阻塞住。解析器被阻塞住以後,瀏覽器會收到第一個渲染請求,「Hi there!」 會被現實在頁面上。這個步驟和剛纔那種狀況是一致的。而後:

複製代碼
1 <html> 2 <body> 3   <link rel="stylesheet" href="example.css"> 4   <div>Hi there!</div> 5   <script> 6     document.write('<script src="other.js"></scr' + 'ipt>'); 7   </script> 8   <div>Hi again!</div> 9   <script src="last.js"></script>
複製代碼

解析器發現了last.js,可是因爲預加載器剛纔已經把它給加載下來了,放在了瀏覽器的緩存裏,因此last.js會被當即執行。以後,瀏覽器會收到渲染請求「Hi again」也被顯示在了頁面上。

經過先後兩種狀況的對比,但願你們能夠對頁面的渲染有必定的瞭解,而後再有針對性的作一些優化。晚安!

相關文章
相關標籤/搜索