Webkit底層原理(5)--CSS解釋器和樣式佈局

從整個網頁的加載和渲染過程來看,CSS解釋器和規則匹配處於DOM樹建立之後,RenderObject樹(下一篇文章會介紹)建立之前,CSS解釋器解釋後的結果會保存起來,然後RenderObject樹基於該結果進行規則匹配和佈局計算。當網頁有用戶交互或者動畫等動作的時候,通過CSSOM技術,JavaScript代碼同樣可以很方便的修改CSS樣式,Webkit此時需要重新解釋樣式並重復以上這一過程。

一、CSSOM(CSS Object Model)

通常我們的CSS代碼都是靜態的,那麼CSS有沒有提供一些方法可以讓開發者寫一些腳本去操作它呢?這就是CSSOM,成爲CSS對象模型。它的思想是在DOM中的一些節點接口中,加入獲取和操作CSS屬性或者接口的JavaScript接口。因而JavaScript可以動態操作CSS樣式。

對於內部和外部樣式表,CSSOM定義了樣式表的接口,稱爲CSSStyleSheet,這是一個可以在JavaScript代碼中訪問的接口。藉助於該接口,開發者可以在JavaScript中獲取樣式表的各種信息,例如CSS的href、樣式表類型type、規則信息cssRules等,甚至可以獲取樣式表中的CSS規則列表。這個接口同DOM中的Script或者Link節點不一樣,它是CSSOM定義的心接口。開發者可以通過document.stylesheets查看當前網頁中包含的所有CSS樣式表,這是因爲CSSOM對DOM中的Document接口進行了擴展。

W3C還定義了另一個規範,那就是CSSOM View,它的基本含義是增加一些新的屬性到Window、Document、Element、HTMLElement和MouseEvent等接口,用於表示跟視圖相關的特徵,例如窗口大小、網頁滾動位置、元素的位置、鼠標事件的座標等信息。

二、CSS解釋器和規則匹配

接下來看一下Webkit如何解釋CSS代碼並選擇相應的規則。

1. 解釋過程

CSS解釋器是指從CSS字符串經過CSS解釋器處理後變成渲染引擎的內部規則表示的過程。這一過程並不複雜,基本的思想是由CSSParser類負責。當Webkit需要解釋CSS內容的時候,調用CSSParser來負責,最後Webkit將創建好的結構設置到StyleSheetContents對象中。

在解釋網頁中自定義的CSS樣式之前,實際上Webkit渲染引擎會爲每個網頁設置一個默認的樣式,這決定了網頁所沒有設置的元素屬性及其屬性默認值和將要顯示的效果。一般來講,不同的Webkit移植可以設置不同的默認樣式。

2. 樣式規則匹配

樣式規則建立完成之後,Webkit保存規則結構在DocumentRuleSets對象中。當DOM的節點建立之後,Webkit會爲其中的一些節點(可視節點)選擇合適的樣式信息。這些工作都是由StyleResolver負責。

基本的思路是使用StyleResolver來爲DOM的元素節點匹配樣式。StyleResolver類根據元素的信息,例如Tag Name、Class等,從樣式規則中查找最匹配的規則,然後將樣式信息保存到新建的RenderStyle中。最後這些RenderStyle對象被RenderObject使用。

樣式的匹配則是由ElementRuleCollector來計算並獲得,它根據元素的屬性等信息,從之前的DocumentRuleSets中獲取規則集合,依次按照ID、Class、Tag等選擇器信息逐次匹配獲得元素的樣式。具體的過程是:

  1. 當Webkit需要爲HTML元素創建RenderObject的時候,首先StyleResolver負責獲取樣式信息,並返回RenderStyle對象,RenderStyle對象包含了匹配完的結果樣式信息;
  2. 根據實際需求,每個元素可能需要匹配不同來源的規則,依次是瀏覽器規則集合、用戶規則集合和HTML網頁中包含的自定義規則集合。這三個規則的匹配方式是類似的,這裏以自定義規則匹配爲例;
  3. 對於自定義規則集合,它先查找ID規則,檢測有無匹配的規則,之後依次檢測類型規則、標籤規則等。如果某個規則匹配上該元素,Webkit把這些規則保存到匹配結果中;
  4. 最後,Webkit對這些規則進行排序。對於該元素需要的樣式屬性,Webkit選擇從高優先級規則中選取,並將樣式屬性返回。

3. JavaScript設置樣式

CSSOM定義了JavaScript訪問樣式的能力和方式。在Webkit中,這需要JavaScript引擎和渲染引擎共同來完成。之後的文章會詳細介紹JavaScript引擎。

大致的過程是,JavaScript引擎調用設置屬性值的公共處理函數,然後該函數調用屬性值解析函數。而後Webkit將解析後的信息設置到元素的style屬性的樣式webkitTransform中,然後設置標記表明該元素需要重新計算樣式,並觸發重新佈局。最後就是Webkit的重新繪製。

三、Webkit佈局

1. 基礎

當Webkit創建RenderObject對象之後,每個對象是不知道自己的位置、大小等信息的,Webkit根據盒模型來計算它們的位置大小信息的過程稱爲佈局計算(排版)。

佈局計算根據其計算的範圍大致可以分爲兩類:

  1. 對整個RenderObject樹進行的計算;
  2. 對RenderObject樹中某個子樹的計算,常見的是文本元素或者overflow:auto;,這種情況一般是其子樹佈局的改變不會影響其周圍元素的佈局,因而不需要重新計算更大範圍內的佈局。

2. 佈局計算

佈局計算是一個遞歸的過程,因爲一個節點的大小通常需要先計算它的子女節點的位置、大小等信息。RenderObject節點計算佈局的主要邏輯都是由RenderObject的layout函數來完成,大致過程如圖:
在這裏插入圖片描述

  1. layout函數會判斷RenderObject節點是否需要重新計算,通常這需要通過檢查數組中相應的標記位、子女是否需要計算佈局來確定;
  2. layout函數會確定網頁的寬度和垂直方向上的外邊距,這是因爲網頁通常是在垂直方向滾動,水平方向儘量不需要滾動;
  3. layout函數會遍歷其每一個子女節點,依次計算它們的佈局。每一個元素會實現自己的layout函數,根據特定的算法來計算該類型元素的佈局。如果頁面元素定義了自己的寬高,Webkit按照定義的寬高來確定元素的大小,而對於文本節點這種內聯元素則需要結合其字號大小以及文字數量來確定其對應的寬高。如果頁面元素所確定的寬高超過了佈局容器所能提供的寬高,同時overflow:visible或者overflow:visible,Webkit會提供滾動條來保證可以顯示所有內容,一般來說頁面元素的寬高是在佈局的時候通過相關計算得出來的。如果元素由子女,則Webkit需要遞歸這一過程;
  4. 節點根據它的子女們的大小計算得出自己的高度,整個過程結束。

哪些情況下需要重新計算佈局呢?總體來講,只要樣式發生變化,Webkit都需要重新計算,有以下一些情況:

  1. 當網頁首次被打開的時候,瀏覽器設置網頁的可視區域(viewport),並調用計算佈局的方法。就是說當可視區域發生變化的時候,Webkit需要重新計算佈局;
  2. 網頁的動畫會觸發佈局計算。當網頁顯示結束之後,動畫可能改變樣式屬性,Webkit就需要重新計算;
  3. JavaScript通過CSSOM直接修改樣式信息,也會觸發Webkit重新計算佈局;
  4. 用戶的交互也會觸發佈局計算,例如滾動網頁。

CSS的佈局計算是以包含塊和盒模型爲基礎的,這表示這些元素的佈局計算都依賴於塊。但是,CSS標準也規定了行內元素,它們和塊元素顯示不太一樣的是它們不會獨佔一行,而是在行內顯示。爲此,Webkit的處理方式是–對於一個塊元素對應的RenderObject對象,它的子女要麼都是塊元素的RenderObject對象,要麼都是非行內元素的RenderObject,這可以通過建立匿名塊(Anonymous Blok)對象來實現,下一篇文章會介紹。

此外,佈局計算是比較耗時間的,更糟糕的是,一旦佈局發生變化,Webkit可能需要後面的重新繪製操作。

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