瀏覽器加載、解析、渲染的過程

爲何要了解瀏覽器加載、解析、渲染這個過程?

1.瞭解瀏覽器加載,能夠在引用外部樣式文件,外部js時,將他們放到合適的位置,使瀏覽器以最快的速度將文件加載完畢javascript

2.瞭解瀏覽器解析,能夠在構建DOM結構,組織css選擇器時,選擇最優的寫法,提升瀏覽器的解析速率。css

3.瞭解瀏覽器渲染,明白渲染的過程,咱們在設置元素屬性,編寫js文件時,能夠減小」重繪「」從新佈局「的消耗。html

瀏覽器是如何進行加載、解析、渲染的呢?

瀏覽器的主要功能是將用戶選擇的web資源呈現出來,它須要從服務器請求資源,並將其顯示在瀏覽器窗口中,資源的格式一般是HTML,也包括PDF、image及其餘格式。用戶用URI(Uniform Resource Identifier統一資源標識符)來指定所請求資源的位置,經過DNS查詢,將網址轉換爲IP地址。整個瀏覽器工做的流程,以前博客中有論述: 
  一、輸入網址。 
  二、瀏覽器查找域名的IP地址。 
  3. 瀏覽器給web服務器發送一個HTTP請求 
  4. 網站服務的永久重定向響應 
  5. 瀏覽器跟蹤重定向地址 如今,瀏覽器知道了要訪問的正確地址,因此它會發送另外一個獲取請求。 
  6. 服務器「處理」請求,服務器接收到獲取請求,而後處理並返回一個響應。 
  7. 服務器發回一個HTML響應 
  8. 瀏覽器開始顯示HTML 
  9. 瀏覽器發送請求,以獲取嵌入在HTML中的對象。在瀏覽器顯示HTML時,它會注意到須要獲取其餘地址內容的標籤。這時,瀏覽器會發送一個獲取請求來從新得到這些文件。這些文件就包括CSS/JS/圖片等資源,這些資源的地址都要經歷一個和HTML讀取相似的過程。因此瀏覽器會在DNS中查找這些域名,發送請求,重定向等等…java

還有是:web

    1. 用戶訪問網頁,DNS服務器(域名解析系統)會根據用戶提供的域名查找對應的IP地址,找到後,系統會向對應IP地址的網絡服務器發送一個http請求。
    2. 網絡服務器解析請求,併發送請求給數據庫服務器。
    3. 數據庫服務器將請求的資源返回給網絡服務器,網絡服務器解析數據,並生成html文件,放入http response中,返回給瀏覽器。
    4. 瀏覽器解析 http response。
      1~4步驟須要瞭解HTTP協議。
      訪問服務器端可能遭遇的問題:若是網絡服務器沒法獲取數據庫服務器返回的資源文件(http response 404),或者因爲併發緣由暫時沒法處理用戶的http請求(http response 500)
    5. 瀏覽器解析 http response後,須要下載html文件,以及html文件內包含的外部引用文件,及文件內涉及的圖片或者多媒體文件。

關於加載順序:當瀏覽器得到一個html文件時,會」自上而下「加載,並在加載過程當中進行解析渲染。數據庫

加載:

即爲獲取資源文件的過程,不一樣瀏覽器,以及他們的不一樣版本在實現這一過程時,會有不一樣的實現效果(資源間互相阻塞)(須要學習使用timeline來作測試)

<!DOCTYPE html>
<html>
     <head>
           <meta charset="utf-8">
           <link rel="stylesheet"  href="test.css"  type="text/css" />
           <script src="test.js" type="text/javascript"></script>
     </head>
     <body>    
           <p>阻塞</p>
           <img src="test.jpg" /> 
     </body>
</html>

加載過程當中遇到外部css文件,瀏覽器另外發出一個請求,來獲取css文件。
遇到圖片資源,瀏覽器會另外發出一個請求,來獲取圖片資源。這是異步請求,並不會影響html文檔進行加載。後端

可是當文檔加載過程當中遇到js文件,html文檔會掛起渲染(加載解析渲染同步)的線程,不只要等待文檔中js文件加載完畢,還要等待解析執行完畢,才能夠恢復html文檔的渲染線程。瀏覽器

      緣由:js有可能會修改DOM,最爲經典的document.write,這意味着,在JS執行完成前,後續全部資源的下載多是沒有必要的,這是js阻塞後續資源下載的根本緣由。
      方法:能夠將外部引用的js文件放在</body>前。服務器

雖然css文件的加載不影響js文件的加載,可是卻影響js文件的執行,即便js文件內只有一行代碼,也會形成阻塞。網絡

     緣由:可能會有 var width = $('#id').width(),這意味着,js代碼執行前,瀏覽器必須保證css文件已下載和解析完成。這也是css阻塞後續js的根本緣由。
     方法:當js文件不須要依賴css文件時,能夠將js文件放在頭部css的前面。

 

除了<link href="" />,內部<style></style> 這種定義,在考慮阻塞時也要考慮。

不要在外部調用的js文件中調用運行時間較長的函數,若是必定要用,可使用setTimeout函數。

   由於:1.瀏覽器GUI渲染線程。

           2.Javascript引擎線程

           3.瀏覽器定時器觸發線程(setTimeout)

           4.瀏覽器事件觸發線程

           5.瀏覽器http異步請求線程

            瀏覽器有以上五個常駐線程,注意:這裏也涉及到阻塞的現象,當js引擎線程(第二個)進行時,會掛起其餘一切線程,這個時候三、四、5這三類線線程也會產生不一樣的異步事件(這句話不懂啊),因爲 javascript引擎線程爲單線程,因此代碼都是先壓到隊列,採用先進先出的方式運行,事件處理函數,timer函數也會壓在隊列中,不斷的從隊頭取出事件,這就叫:javascript-event-loop。

 

解析

html文檔解析生成解析樹,即dom樹,是有dom元素及屬性節點組成,樹的根是document對象。

    DOM:文檔對象模型的縮寫,是html文檔的對象表示,做爲html的外部接口供js調用。

    document.getElementById('test').style.display="none";//經過dom接口將id爲test的display值設爲none。

css解析將css文件解析爲樣式表對象。該對象包含css規則,該規則包含選擇器和聲明對象。

          

渲染:

當瀏覽器得到一個html文件時,會「自上而下」加載,並在加載過程當中進行解析渲染。 
解析: 
       1. 瀏覽器會將HTML解析成一個DOM樹,DOM 樹的構建過程是一個深度遍歷過程:當前節點的全部子節點都構建好後纔會去構建當前節點的下一個兄弟節點。 
       2. 將CSS解析成 CSS Rule Tree 。 
       3. 根據DOM樹和CSSOM來構造 Rendering Tree。注意:Rendering Tree 渲染樹並不等同於 DOM 樹,由於一些像 Header 或 display:none 的東西就不必放在渲染  樹中了。

     

        4.有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的css定義以及他們的從屬關係。下一步操做稱之爲Layout,計算出每一個節點在屏幕中的位置,

        5.繪製,即遍歷render樹,並使用UI後端繪製每一個節點。

   上述過程是逐漸完成的,爲了更好的用戶體驗,渲染引擎將會盡量早的將內容呈如今屏幕上,並不會等到全部的html都解析完成以後再去構建和佈局render樹。它是解析完一部份內容就顯示一部份內容,同時,可能經過網絡下載其他內容。

幾個概念:

(1)Reflow(迴流):瀏覽器要花時間去渲染,當它發現某個部分發生了變化影響,那就須要倒回去從新渲染。

(2)Repaint(重繪):若是知識改變了某個元素的背景顏色,文字顏色等,不影響元素周圍或內部佈局的屬性,將只會引發瀏覽器的repaint,重畫某一部分

 Reflow要比Repaint更花費時間,也就更影響性能。在寫代碼的時候儘可能避免過多的Reflow。

 reflow的緣由:

  (1)頁面初始化的時候; 
  (2)操做DOM時; 
  (3)某些元素的尺寸變了; 
  (4)若是 CSS 的屬性發生變化了。

 減小 reflow/repaint

 (1)不要一條一條地修改 DOM 的樣式。與其這樣,還不如預先定義好 css 的 class,而後修改 DOM 的 className。 
 (2)不要把 DOM 結點的屬性值放在一個循環裏當成循環裏的變量。 
 (3)爲動畫的 HTML 元件使用 fixed 或 absoult 的 position,那麼修改他們的 CSS 是不會 reflow 的。 
 (4)千萬不要使用 table 佈局。由於可能很小的一個小改動會形成整個 table 的從新佈局。

html頁面加載和解析流程:

      1. 用戶輸入網址(假設是個html頁面,而且是第一次訪問),瀏覽器向服務器發出請求,服務器返回html文件; 
      2. 瀏覽器開始載入html代碼,發現<head>標籤內有一個<link>標籤引用外部CSS文件; 
      3. 瀏覽器又發出CSS文件的請求,服務器返回這個CSS文件; 
      4. 瀏覽器繼續載入html中<body>部分的代碼,而且CSS文件已經拿到手了,能夠開始渲染頁面了; 
      5. 瀏覽器在代碼中發現一個<img>標籤引用了一張圖片,向服務器發出請求。此時瀏覽器不會等到圖片下載完,而是繼續渲染後面的代碼; 
      6. 服務器返回圖片文件,因爲圖片佔用了必定面積,影響了後面段落的排布,所以瀏覽器須要回過頭來從新渲染這部分代碼; 
      7. 瀏覽器發現了一個包含一行Javascript代碼的<script>標籤,趕快運行它; 
      8. Javascript腳本執行了這條語句,它命令瀏覽器隱藏掉代碼中的某個<div> (style.display=」none」)。忽然少了這麼一個元素,瀏覽器不得不從新渲染這部分代碼; 
      9. 終於等到了</html>的到來,瀏覽器淚流滿面…… 
      10. 等等,還沒完,用戶點了一下界面中的「換膚」按鈕,Javascript讓瀏覽器換了一下<link>標籤的CSS路徑; 
      11. 瀏覽器召集了在座的各位<div><span><ul><li>們,「大夥兒收拾收拾行李,咱得從新來過……」,瀏覽器向服務器請求了新的CSS文件,從新渲染頁面。

 

關於script標籤的位置:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>測試js代碼位置</title>
    <script type="text/javascript">
        var item = document.getElementById("item");
        cosole.log(item);
    </script>

</head>
<body>
    <div id="item" width="100px" height="100px">
        你好
    </div>

</body>
</html>

上述代碼中有一段js代碼,要在控制檯打印一個元素,我把script標籤放在head裏,控制檯裏打印出來的是null。 

又把js代碼放在body結束標籤以前,打印出來的就是div元素了:

這個簡單的例子咱們能夠看到,js代碼在加載完後,是當即執行的。 

 

我又作了一個測試,在js代碼裏面寫了一個死循環,把它放在head標籤中

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>測試js代碼位置</title>
    <script type="text/javascript">
        var item = document.getElementById("item");
        while(true){
            console.log(1);
        }
    </script>   
</head>
<body>
    <div id="item" width="100px" height="100px">
        你好
    </div>  
</body>
</html>

一直在執行那個打印1的死循環,後面的body都沒有加載渲染出來。因此,這個小例子,咱們能夠看出,js的下載和執行會阻塞Dom樹的構建。

 

 

 Javascript的加載和執行的特色: 
(1)載入後立刻執行; 
(2)執行時會阻塞頁面後續的內容(包括頁面的渲染、其它資源的下載)。緣由:由於瀏覽器須要一個穩定的DOM樹結構,而JS中頗有可能有 代碼直接改變了DOM樹結構,好比使用 document.write 或 appendChild,甚至是直接使用的location.href進行跳轉,瀏覽器爲了防止出現JS修 改DOM樹,須要從新構建DOM樹的狀況,因此 就會阻塞其餘的下載和呈現。

   

減小 JavaScript 對性能的影響的方法:

    1. 將全部的script標籤放到頁面底部,也就是body閉合標籤以前,這能確保在腳本執行前頁面已經完成了DOM樹渲染。
    2. 儘量地合併腳本。頁面中的script標籤越少,加載也就越快,響應也越迅速。不管是外鏈腳本仍是內嵌腳本都是如此。
    3. 採用無阻塞下載 JavaScript 腳本的方法: (1)使用script標籤的 defer 屬性(僅適用於 IE 和 Firefox 3.5 以上版本); (2)使用動態建立的script元素來下載並執行代碼;
相關文章
相關標籤/搜索