個人日記022--瀏覽器渲染

一直想整理一下瀏覽器渲染方面的相關知識的,可是看到下面這篇文章挺好的,就直接轉載了

       寫了一段時間的前端頁面了,對於瀏覽器中頁面的渲染流程和原理還有點一知半解,所以就花時間去學習瞭解了下瀏覽器的渲染原理,這邊對自己的理解總結一下,如有錯誤請指正。

一、瀏覽器簡介

        瀏覽器是使用最廣泛的軟件之一,主要功能是向服務器發出請求,在瀏覽器窗口中展示用戶需要的網絡資源。資源的位置由用戶的的URI(Uniform Resource Identifier統一資源標識符)來指定,通過DNS查詢,將網址轉換爲IP地址。資源的格式通常是HTML,也包括PDF、image及其他格式。整個瀏覽器工作的流程,主要如下: 

        用戶輸入網址——瀏覽器查找IP地址——發送HTTP請求——服務器處理請求並響應——服務器發回HTML響應——瀏覽器開始解析HTML——瀏覽器發送請求獲取HTML中內嵌的對象,如CSS/JS/圖片等資源——瀏覽器展示完整頁面

      本次介紹的主要就是瀏覽器從接收到服務器響應的HTML到展示完整頁面的整個過程,下面開始——

二、 瀏覽器渲染流程

       先來一張瀏覽器渲染流程圖

    從這張經典的圖中可以看出以下幾點:

    1,瀏覽器可以解析的資源,HTML,SVG,XHTML等,解析完會生成DOM Tree。

    2,CSS資源會解析成CSS Rule Tree。

    3,JS通過DOM API和CSSOM API來操作DOM樹和CSS樹。

    4,解析完成後綜合DOM樹和CSS樹會生成Rendering Tree,計算每個元素(Frame)的位置,這個過程就是layout或者叫reflow過程。

    5,調用操作系統Native GUI的API繪製。

注意:上述這個過程是理論上是逐步完成的,但是實際實現中爲了更好的用戶體驗,渲染引擎爲了儘可能早的將內容呈現到屏幕上,會在構建DOM樹的同時去解析CSS構建CSS樹,並且還會去生成Rendering Tree。解析完一部分內容就顯示一部分內容,同時,可能還在通過網絡下載其餘內容,這樣就可以更快的顯示出頁面,其中解析後面的內容涉及到佈局和樣式的改變引起的reflow過程和repaint,我們後面在詳細說明。

        接下來我們一步步詳細說明:

三、HTML解析與DOM樹構建

       HTML解析這方面沒啥好說的,大致流程是瀏覽器使用詞法分析器和解析器將HTML內容解析成爲語法樹,也就是DOM樹,DOM 樹的構建過程是一個深度遍歷過程:當前節點的所有子節點都構建好後纔會去構建當前節點的下一個兄弟節點。

        DOM樹是由DOM元素和屬性節點組成,DOM是文檔對象模型(Document ObjectModel)的縮寫,是HTML文檔的對象表示,同時也是外部內容與HTML元素之間的藉口。

        具體解析與構建過程如下:

在這裏插入圖片描述

 

四、 CSS解析

        CSS解析的過程類似於HTML解析,也是瀏覽器使用自帶的解析器進行解析,一般解析過程是由上而下,會將CSS文件解析成爲StyleSheet對象,且每個對象都包含CSS規則。CSS規則對象包含了選擇和聲明對象,以及其他與CSS語法對應的對象。CSS解析完成後會大致生成如下結構的CSS Rule Tree。

 

五、渲染

        渲染的主要過程分爲——Render Tree(渲染樹)生成——Layout(佈局)——Paint(繪製)。

        1,Render Tree的生成

        DOM樹和CSS樹結合生成Render Tree(渲染樹)——這是由可視化元素按照其顯示順序組成的樹形結構,是文檔可視化的表示,它的作用是讓瀏覽器能夠按照正確的順序渲染頁面元素。Firefox中稱之爲“框架”,Webkit中的術語則是呈現器或者呈現對象。
        渲染樹是和DOM元素相對應的,但是並非全部一一對應,例如:1,非可視化元素是不會出現在渲染樹中,如“head”元素,2,如果元素的display屬性值爲“none”,也不會出現在渲染樹中(但是visibility屬性值爲“hidden”的元素會出現在渲染樹中)。詳細的流程請參考本文最後參考資料中的介紹。

        2,佈局

        渲染樹中並不包含位置和大小的信息,計算這些值的過程就是佈局或者重排。

        佈局的過程是一個遞歸的過程,從根元素開始,遞歸遍歷部分或者所有的渲染樹結構,併爲每一個需要顯示元素計算幾何信息。一般根元素位置座標(0,0),大小爲瀏覽器窗口的可見區域。

        這裏涉及到兩個重要的概念reflowrepaint

        repaint(重繪):元素的某一部分屬性發生改變,如字體顏色,背景顏色等改變,尺寸並未改變,這時發生的改變過程就是repaint。

 

        reflow(迴流): 因爲瀏覽器渲染是一個由上而下的過程,當發現某部分的變化影響了佈局時,就需要倒回去重新渲染,這個過程就稱之爲reflow。reflow幾乎是沒法避免的,現在一些常用的效果,比如樹狀目錄的摺疊、展開(實質上是元素的顯示與隱藏)等,都將引起瀏覽器的 reflow。鼠標滑過、點擊……只要這些行爲引起了頁面上某些元素的佔位面積、定位方式、邊距等屬性的變化,都會引起它內部、周圍甚至整個頁面的重新渲染。基本上能引起reflow的主要有幾個原因:

        1,網頁初始化。

        2,JS操作DOM樹的時候,增加刪除元素等。

        3,某些元素的尺寸改變。

        4,CSS屬性的改變,

        但是瀏覽器很聰明,爲了避免細小的改變就進行repaint或者reflow,瀏覽器採用一種"dirty"系統,會將這些改變操作積攢一批,然後做一次reflow,這又叫異步reflow或增量異步reflow。但是有些特殊情況不會這麼做,比如:resize窗口,改變了頁面默認的字體,等,對於這些操作,瀏覽器會馬上進行reflow。

        但是有的時候,我們自己編寫的腳本會阻止瀏覽器的這種操作,比如我們請求下面的值的時候:offsetTop, offsetLeft, offsetWidth, offsetHeight,scrollTop/Left/Width/Height,clientTop/Left/Width/Height,IE中的 getComputedStyle(), 或 currentStyle等,如果我們的程序運行的時候需要這些值,那麼瀏覽器需要給我們返回最新的值,而這樣就會將當前積攢的操作執行,從而引起頻繁的reflow或者repaint。

        通常reflow比repaint會耗費更多的時間,從而也就會影響性能,所以編寫代碼的時候要儘可能避免過多的reflow或者repaint。減少reflow/repaint的方法:

        1,修改樣式不要逐條修改,建議定義CSS樣式的class,然後直接修改元素的className。

        2,不要將DOM節點的屬性值放在循環中當成循環的變量。

        3,爲動畫的 HTML 元素使用 fixed 或 absoult 的 position,那麼修改他們的 CSS 是不會 reflow 的。

        4,把DOM離線後修改。如設置DOM的display:none,然後進行你需要的多次修改,然後再顯示出來,或者clone一個節點到內存中,然後隨意修改,修改完成後再與在線的交換。

        5,千萬不要使用table佈局,一個微小的改變就可能引起整個table的重新佈局。

        3,繪製

        在繪製階段,系統會遍歷渲染樹,並且調用呈現器將的“paint”方法,將內容顯示在屏幕上。同樣,類似於佈局過程,也分爲全局和增量兩種。更多繪製詳情參考文末資料。

六、性能優化

 1,提升HTML加載速度

        -    頁面精簡,刪除不必要的註釋,空格,將內嵌的JS和CSS移至外部文件,使用壓縮工具等。

        -    減少文件數量,減少頁面上引入的文件數量可以減少請求的次數,可以合併的JS和CSS文件儘量合併。

        -    減少域名查詢,DNS查詢和解析域名需要消耗時間,減少對外部JavaScript、CSS、圖片等資源的引用,不同域名的使用越少越好

        -    使用緩存,重用數據

        -    優化頁面元素的加載順序

        -    使用現在CSS和合法的標籤

        -    指定圖片的大小,如果瀏覽可以立即確定圖片大小就不需要重新進行佈局操作

        -   根據瀏覽器類型選擇合適的策略

        -    使用壓縮工具等。

        -    頁面精簡,刪除不必要的註釋,空格,將內嵌的JS和CSS移至外部文件,使用壓縮工具等。

        2,編寫合理的CSS

        首先說明CSS選擇符的匹配順序,從右到左!從右到左!從右到左!(重要的事情說三遍),所以,類似於“#nav li” 我們以爲很簡單的規則,應該馬上就可以匹配成功,但是,需要從右往左匹配,所以,先會去查找所有的li,然後再去確定它的父元素是不是#nav。因此,編寫合理的CSS也可以提高我們的頁面行能:

        -    DOM的深度儘量淺,不要嵌套過深。

        -    減少inline javascript  css的數量。

        -    使用合法的CSS屬性。

        -    不要爲ID選擇器指定類名或者標籤名。

        -    避免後代選擇器,儘量使用子選擇器。

        -    避免使用通配符。

        3,關於javascript標籤

        對於javascript標籤首先得了解其加載和執行的特點:1,載入後立即執行,2,執行時會阻塞頁面後續的內容,針對這些特點,我們使用javascript標籤時應該注意: 

        -    將所有的javascript標籤放在頁面底部,也就是body標籤閉合之前,這樣可以保證腳本執行前已完成DOM渲染。

        -    儘可能合併腳本,頁面中引入的腳本越少,加載響應速度也就越快。

        -    減少inline javascript的使用。

        -    所有的javascript標籤會按照其引入順序依次執行,只有前面的內容解析完成纔會解析下一個,所以注意多個javascript標籤的引入順序。

        -    使用defer屬性,該屬性可以使腳本在文檔完全呈現以後再執行。

        -    使用async屬性,可以使當前腳本不必等待其他腳本的執行,也不必阻塞文檔的呈現。

七、 結語

       關於瀏覽器渲染的內容基本就是這些了,下面引用網上一篇比較好相關文章中的一段做個總結:

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文件,重新渲染頁面

如果需要了解更多詳細的內容,請參閱下面的資料。如果需要了解更多詳細的內容,請參閱下面的資料。

     參考資料:

     1,權威資料:how browsers work(英文,特別長,但是很權威);

      2,如果看英文有困難的可以看看下面的翻譯版本——瀏覽器工作原理簡介。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章