從基礎做起--瀏覽器渲染

    作爲一個小白很早就關注前端的相關知識,雖然在各類博客汲取不少知識,但感覺還是需要親自整理一些知識點,才能更加熟練

用學到手的東西。在去年校招期間有招聘方涉及到瀏覽器的渲染,當時只懂一點js+css,瞭解的不夠深入,儘管在有大神整理出

清晰的文章,在下還是決定親自讀一下《瀏覽器工作原理》,話不多說,先從瀏覽器構成開始。


    瀏覽器的主要構成(High Level Structure


         瀏覽器的主要組件包括:

        1. 用戶界面 - 包括地址欄、後退/前進按鈕、書籤目錄等,也就是你所看到的除了用
            來顯示你所請求頁面的主窗口之外的其他部分。

        2. 瀏覽器引擎 - 用來查詢及操作渲染引擎的接口。

        3. 渲染引擎 - 用來顯示請求的內容,例如,如果請求內容爲html,它負責解析html
            及css,並將解析後的結果顯示出來。

        4. 網絡 - 用來完成網絡調用,例如http請求,它具有平臺無關的接口,可以在不同
            平臺上工作。

        5. UI後端 - 用來繪製類似組合選擇框及對話框等基本組件,具有不特定於某個平臺
            的通用接口,底層使用操作系統的用戶接口。

        6. JS解釋器 - 用來解釋執行JS代碼。

        7. 數據存儲 - 屬於持久層,瀏覽器需要在硬盤中保存類似cookie的各種數據,
            HTML5定義了web database技術,這是一種輕量級完整的客戶端存儲技術。

        如圖 1.1 所示


 圖 1.1


         在衆多瀏覽器中,Chrome 瀏覽器爲每個Tab分配了各自的渲染引擎實例,每個Tab就是一個獨立的進程。什麼是渲染引擎?


    渲染引擎(The rendering engine


     主流程(The main flow)

        在取得通過網絡請求的文檔以後,渲染引擎的基本流程:


         解析html構建dom

                       ↓

             構建render

                       ↓

             佈局render

                       ↓

             繪製render 樹


 如圖 1.2 所示


        

                                                                                                                 圖1.2


       渲染伊始,解析html標籤,生成DOM結構,然後解析CSS層疊樣式表,根據結果構建Render樹Render樹由一些包含有顏色和大小等屬性的矩

形組成,按順序排列,然後開始佈局,確定結點的座標,最後繪製,遍歷Render樹,使用UI後端繪製。

       注意這個過程是邊解析邊顯示的,並非解析完所有結構才顯示網頁內容。


   渲染樹和Dom樹的關係(The render tree relation to the DOM tree


        渲染對象和元素相對應,但這種對應關係不是一對一的,不可見的DOM元素不會被插入渲染樹,例如head元素。另

外,display屬性爲none的元素也不會在渲染樹中出現visibility屬性爲hidden的元素將出現在渲染樹中)。

        還有一些DOM元素對應幾個可見對象,它們一般是一些具有複雜結構的元素,無法用一個矩形來描述。例如,select元素有三

個渲染對象——一個顯示區域、一個下拉列表及一個按鈕。同樣,當文本因爲寬度不夠而折行時,新行將作爲額外的渲染元素被添

加。另一個多個渲染對象的例子是不規範的html,根據CSS規範,一個行內元素只能僅包含行內元素或僅包含塊狀元素,在存在混

合內容時,將會創建匿名的塊狀渲染對象包裹住行內元素。一些渲染對象和所對應的DOM節點不在樹上相同的位置,例如,浮動和

絕對定位的元素在文本流之外,在兩棵樹上的位置不同,渲染樹上標識出真實的結構,並用一個佔位結構標識出它們原來的位置。


        如圖 1.3 所示


                                                                                                               圖 1.3


   創建樹的流程(The flow of constructing the tree

        處理htmlbody標籤將構建渲染樹的根,這個根渲染對象對應被 CSS規範稱爲containing block的元素——包含了其他所有

塊元素的頂級塊元素。它的大小就是viewport——瀏覽器窗口的顯示區域,文檔指向的渲染對象。

    樣式計算(Style Computation 

        樣式數據是非常大的結構,保存大量的樣式屬性會帶來內存問題。不進行優化,找到每個元素匹配的規則會導致性能問題。

    Specifity

        CSS2規範中定義的選擇符specifity如下:

        如果聲明來自style屬性,而不是一個選擇器的規則,則計1,否則計0(=a

        計算選擇器中id屬性的數量(=b

        計算選擇器中class及僞類的數量(=c

        計算選擇器中元素名及僞元素的數量(=d

        連接abcd四個數量(用一個大基數的計算系統)將得到specifity,根據改值確定優先級。


    佈局

        當渲染對象被創建並添加到樹中,它們並沒有位置和大小,計算這些值的過程稱爲layout(webkit) reflow(Gecko)

Html使用基於流的佈局模型,意味着大部分時間,可以以單一的途徑進行幾何計算。流中靠後的元素並不會影響前面元素的幾何特

性,所以佈局可以在文檔中從右向左、自上而下的進行。也存在一些例外,比如html tables

        座標系統相對於根frame,使用topleft座標。佈局是一個遞歸的過程,由根渲染對象開始,它對應html文檔元素,佈局

繼續遞歸的通過一些或所有的通過一些或所有的frame層級,爲每個需要幾何信息的渲染對象進行計算。所有的渲染對象都有一

layoutreflow方法,每個渲染對象調用需要佈局的childrenlayout方法。

    Dirty bit系統

        爲了不因爲每個小變化都全部重新佈局,瀏覽器使用一個dirty bit系統,一個渲染對象發生了變化或是被添加了,就標記它及

它的childrendirty——需要layout。存在兩個標識——個標識——dirtychildren are dirtychildren are dirty說明即使

這個渲染對象可能沒問題,但它至少有一個沒問題,但它至少有一個child需要layout。

    全局和增量layout

        當layout在整棵渲染樹觸發時,稱爲全局layout,這可能在下面這些情況下發生:

        1.一個全局的樣式改變影響所有的渲染對象,比如字號的改變。

        2.窗口resize

        layout也可以是增量的,這樣只有標誌爲dirty的渲染對象會重新佈局(也將導致一些額外的佈局)。增量layout會在渲染

對象dirty時異步觸發,例如,當網絡接收到新的內容並添加到容並添加到DOM樹後,新的渲染對象會添加到渲染樹中。


   layout過程

        layout一般有下面這幾個部分:

        1. parent渲染對象決定它的寬度

        2. parent渲染對象讀取chilidren,並:

        a.放置child渲染對象(設置它的xy)。

        b.在需要時(它們當前爲dirty或是處於全局layout或者其他原因)調用child渲染對象的layout,這將計算child的高度

        c. parent 渲染對象使用 child 渲染對象的累積高度,以及marginpadding的高度來設置自己的高度-這將被parent渲染

            對象的 parent 使用。

        d.dirty標識設置爲false。

    Line breaking

        當一個渲染對象在佈局過程中折行時,則暫停告訴它的 parent 需要折行,parent將創建額外的渲染對象並調用它的layout。


    繪製(Painting

        繪製階段,遍歷渲染樹並調用渲染對象的 paint 方法將它們的內容顯示在屏幕上,繪製使用UI基礎組件。

    全局和增量

        和佈局一樣,繪製也可以是全局的——繪製完整的樹——或增量的。在增量的繪製過程中,一些渲染對象以不影響整棵樹的方

式改變,改變的渲染對象使其在屏幕上的矩形區域失效,這將導致操作系統將其看作dirty區域,併產生一個paint事件,操作系

統很巧妙的處理這個過程,並將多個區域合併爲一個。Chrome中,這個過程更復雜些,因爲渲染對象在不同的進程中,而不是在

主進程中。Chrome在一定程度上模擬操作系統的行爲,表現爲監聽事件並派發消息給渲染根,在樹中查找到相關的渲染對象,重

繪這個對象(往往還包括它的 children)。

    繪製順序

         這個是元素壓入堆棧的順序,這個順序影響着繪製,堆棧從後向前進行繪製。

一個塊渲染對象的堆棧順序是:

         1. 背景色

         2. 背景圖

         3. border

         4. children

         5. outline


    渲染引擎的線程

        渲染引擎是單線程的,除了網絡操作以外,幾乎所有的事情都在單一的線程中處理,在FirefoxSafari中,這是瀏覽器的主

線程,Chrome中這是tab的主線程。網絡操作由幾個並行線程執行,並行連接的個數是受限的(通常是26個)。


    定位策略Position scheme

        這裏有三種策略:

        1. normal-對象根據它在文檔中位置定位,這意味它在渲染樹和在DOM樹中位置一致,根據它的盒模型和大小進行佈局。

        2. float-對象先像普通流一樣佈局,然後儘可能的向左或是向右移動

        3. absolute-對象在渲染樹中的位置和DOM樹中位置無關。

        staticrelativenormalabsolutefixed屬於absolutestatic定位中,不定義位置而使用默認的位置。其他策

略中,作者指定位置——top、bottomleftright


    Layered representation

        這個由CSS屬性中的z-index指定,表示盒模型的第三個大小,即在z軸上的位置。Box分發到堆棧中(稱爲堆棧上文),

每個堆棧中靠後的元素將被較早繪製,棧頂靠前的元素離用戶最近,當發生交疊時,將隱藏靠後的元素。堆棧根據z-index屬性排

序,擁有z-index屬性的box形成了一個局部堆棧,viewport有外部堆棧。