瀏覽器渲染網頁的過程

瀏覽器的主要功能是將用戶選擇的 web 資源呈現出來,它須要從服務器請求資源,並將其顯示在瀏覽器窗口中,資源的格式一般是 HTML,也包括 PDF、image 及其餘格式。css

瀏覽器的線程

瀏覽器是多線程的,它們在內核制控下相互配合以保持同步。一個瀏覽器至少實現三個常駐線程:JavaScript 引擎線程,GUI 渲染線程,瀏覽器事件觸發線程。html

  • GUI 渲染線程:負責渲染瀏覽器界面 HTML 元素,當界面須要重繪(Repaint)或因爲某種操做引起迴流(reflow)時,該線程就會執行。在 Javascript 引擎運行腳本期間,GUI 渲染線程都是處於掛起狀態的,也就是說被」凍結」了。web

  • JavaScript 引擎線程:主要負責處理 Javascript 腳本程序ajax

  • 定時器觸發線程:瀏覽器定時計數器並非由 JavaScript 引擎計數的, JavaScript 引擎是單線程的, 若是處於阻塞線程狀態就會影響記計時的準確, 所以瀏覽器經過單獨線程來計時並觸發定時。瀏覽器

  • 事件觸發線程:當一個事件被觸發時該線程會把事件添加到待處理隊列的隊尾,等待 JS 引擎的處理。這些事件包括當前執行的代碼塊如定時任務、瀏覽器內核的其餘線程如鼠標點擊、AJAX 異步請求等。因爲 JS 的單線程關係全部這些事件都得排隊等待 JS 引擎處理。定時塊任何和 ajax 請求等這些異步任務,事件觸發線程只是在到達定時時間或者是 ajax 請求成功後,把回調函數放到事件隊列當中。緩存

  • 異步 HTTP 請求線程:在 XMLHttpRequest 在鏈接後是經過瀏覽器新開一個線程請求, 將檢測到狀態變動時,若是設置有回調函數,異步線程就產生狀態變動事件放到 JavaScript 引擎的處理隊列中等待處理。在發起了一個異步請求時,http 請求線程則負責去請求服務器,有了響應之後,事件觸發線程再把回到函數放到事件隊列當中。性能優化

瀏覽器渲染流程

1.解析 html,構建 dom 樹;解析 CSS 會產生 CSS 規則樹

瀏覽器解析 HTML 文檔的源碼,而後構造出一個 DOM 樹,DOM 樹的構建過程是一個深度遍歷的過程,當前節點的全部子節點都構建好之後纔會去構建當前節點的下一個兄弟節點。服務器

瀏覽器對 CSS 文件內容進行解析,通常來講,瀏覽器會先查找內聯樣式,而後是 CSS 文件中定義的樣式,最後再是瀏覽器默認的樣式,構建 CSS Rule Tree。多線程

2.構建(construct) render 樹

根據 DOM 樹和 CSSOM 樹來構造 Rendering Tree。注意:Rendering Tree 渲染樹並不等同於 DOM 樹,由於一些像 Header 或 display:none 的東西就不必放在渲染樹中了。dom

3.佈局(layout) render 樹

有了 Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的 CSS 定義以及他們的從屬關係,從而去計算出每一個節點在屏幕中的位置。

4.繪製(painting) render 樹

按照算出來的規則,經過顯卡,把內容畫到屏幕上。

5.迴流(reflow)

當瀏覽器發現某個部分發生了點變化影響了佈局,須要倒回去從新渲染,內行稱這個回退的過程叫 reflow。reflow 會從 這個 root frame 開始遞歸往下,依次計算全部的結點幾何尺寸和位置。

6.重繪(repaint)

改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性時,屏幕的一部分要重畫,可是元素的幾何尺寸沒有變。

瀏覽器對 CSS 和 JS 的解析規則

CSS:

  • CSS 放在 head 中會阻塞頁面的渲染(頁面的渲染會等到 css 加載完成)
  • CSS 阻塞 JS 的執行 (由於 GUI 線程和 JS 線程是互斥的,由於有可能 JS 會操做 CSS)
  • CSS 不阻塞外部腳本的加載(不阻塞 JS 的加載,但阻塞 JS 的執行,由於瀏覽器都會有預先掃描器)

JS:

  • 直接引入的 JS 會阻塞頁面的渲染(GUI 線程和 JS 線程互斥)
  • 異步加載的 JS (script 標籤中添加 defer 屬性)不阻塞頁面的解析
  • 異步加載的 JS (script 標籤中添加 async 屬性)下載過程不阻塞頁面的解析,下載完成後當即執行,執行過程會阻塞頁面的解析
  • JS 不阻塞資源的加載
  • JS 順序執行,阻塞後續 JS 邏輯的執行

HTML 頁面加載和解析流程

  1. 用戶輸入網址(假設是個 html 頁面,而且是第一次訪問),瀏覽器向服務器發出請求,服務器返回 html 文件;
  2. 瀏覽器開始載入 html 代碼,發現< head >標籤內有一個< link >標籤引用外部 CSS 文件;
  3. 瀏覽器發出 CSS 文件的請求,服務器返回這個 CSS 文件;(同時 GUI 渲染線程繼續執行,互不影響)
  4. 瀏覽器繼續載入 html 中< body >部分的代碼,而且 CSS 文件已經拿到手了,開始渲染頁面;
  5. 瀏覽器在代碼中發現一個< img >標籤引用了一張圖片,向服務器發出請求。此時瀏覽器不會等到圖片下載完,而是繼續渲染後面的代碼;
  6. 服務器返回圖片文件,因爲圖片佔用了必定面積,影響了後面段落的排布,所以瀏覽器須要回過頭來從新渲染這部分代碼;
  7. 瀏覽器發現了一個包含一行 Javascript 代碼的< script >標籤,直接運行;
  8. Javascript 腳本操做某個元素,它命令瀏覽器隱藏掉代碼中的某個標籤 (style.display=」none」)。瀏覽器會從新渲染這部分代碼;
  9. 遇到 </html >,流程結束;
  10. 當用戶操做,頁面產生交互,瀏覽器發現某個部分發生了點變化影響了佈局,瀏覽器迴流(reflow);改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性時,瀏覽器重繪(repaint)。

HTML 頁面加載優化

  • 頁面減肥。頁面的肥瘦是影響加載速度最重要的因素刪除沒必要要的空格、註釋。將 inline 的 script 和 css 移到外部文件,可使用 HTML Tidy 來給 HTML 減肥,還可使用一些壓縮工具來給 JavaScript 減肥;

  • 減小文件數量。減小頁面上引用的文件數量能夠減小 HTTP 鏈接數。許多 JavaScript、CSS 文件能夠合併最好合並;

  • 減小域名查詢。DNS 查詢和解析域名也是消耗時間的,因此要減小對外部 JavaScript、CSS、圖片等資源的引用,不一樣域名的使用越少越好;

  • 緩存重用數據;

  • 優化頁面元素加載順序。首先加載頁面最初顯示的內容和與之相關的 JavaScript 和 CSS,而後加載 DHTML 相關的東西,像什麼不是最初顯示相關的圖片、flash、視頻等很肥的資源就最後加載;

  • 減小 inline JavaScript 的數量。瀏覽器 parser 會假設 inline JavaScript 會改變頁面結構,因此使用 inline JavaScript 開銷較大,不要使用 document.write()這種輸出內容的方法,使用現代 W3C DOM 方法來爲現代瀏覽器處理頁面內容;

  • 使用現代 CSS 和合法的標籤。使用現代 CSS 來減小標籤和圖像,例如使用現代 CSS+文字徹底能夠替代一些只有文字的圖片,使用合法的標籤避免瀏覽器解析 HTML 時作「error correction」等操做,還能夠被 HTML Tidy 來給 HTML 減肥;

  • 不要使用嵌套 tables;

  • 指定圖像和 tables 的大小。若是瀏覽器能夠當即決定圖像或 tables 的大小,那麼它就能夠立刻顯示頁面而不要從新作一些佈局安排的工做,這不只加快了頁面的顯示,也預防了頁面完成加載後佈局的一些不當的改變。

參考文章

  1. 網頁在瀏覽器上的渲染過程
  2. 性能優化——CSS和JS的加載和執行
  3. 瀏覽器渲染原理及流程
相關文章
相關標籤/搜索