?從Chrome源碼看瀏覽器如何計算CSS - 前端加油站 - 科蟻網

從Chrome源碼看瀏覽器如何計算CSS

在《Effective前端6:避免頁面卡頓》這篇里面介紹了瀏覽器渲染頁面的過程:

并且《從Chrome源碼看瀏覽器如何構建DOM樹》介紹了第一步如何解析Html構建DOM樹,這個過程大概如下:

瀏覽器每收到一段html的文本之后,就會把它序列化成一個個的tokens,依次遍歷這些token,實例化成對應的html結點并插入到DOM樹里面。

我將在這一篇介紹第二步Style的過程,即CSS的處理。

1. 加載CSS

在構建DOM的過程中,如果遇到link的標簽,當把它插到DOM里面之后,就會觸發資源加載——根據href指明的鏈接:

上面的rel指明了它是一個樣式文件。這個加載是異步,不會影響DOM樹的構建,只是說在CSS沒處理好之前,構建好的DOM并不會顯示出來。用以下的html和css做試驗:

demo.css如下:

從打印的log可以看出(添加打印的源碼略):

[DocumentLoader.cpp(558)] “\n\n\n \n\n\n

\n   

hello, world

\n
\n\n\n”

[HTMLDocumentParser.cpp(765)] “tagName: html  |type: DOCTYPE|attr:              |text: “

[HTMLDocumentParser.cpp(765)] “tagName:            |type: Character |attr:              |text: \n”

[HTMLDocumentParser.cpp(765)] “tagName: html  |type: startTag    |attr:              |text: “

[HTMLDocumentParser.cpp(765)] “tagName: html  |type: EndTag     |attr:              |text: “

[HTMLDocumentParser.cpp(765)] “tagName:            |type: EndOfFile|attr:              |text: “

[Document.cpp(1231)] readystatechange to Interactive

[CSSParserImpl.cpp(217)] recieved and parsing stylesheet: “.text{\n   font-size: 20px;\n}\n.text p{\n    color: #505050;\n}\n”

在CSS沒有加載好之前,DOM樹已經構建好了。為什么DOM構建好了不把html放出來,因為沒有樣式的html直接放出來,給人看到的頁面將會是亂的。所以CSS不能太大,頁面一打開將會停留較長時間的白屏,所以把圖片/字體等轉成base64放到CSS里面是一種不太推薦的做法。

2. 解析CSS

(1)字符串 -> tokens

CSS解析和html解析有比較像的地方,都是先格式化成tokens。CSS token定義了很多種類型,如下的CSS會被拆成這么多個token:

經常看到有人建議CSS的色值使用16位的數字會優于使用rgb的表示,這個是子虛烏有,還是有根據的呢?

如下所示:

如果改成rgb,它將變成一個函數類型的token,這個函數需要再計算一下。從這里看的話,使用16位色值確實比使用rgb好。

(2)tokens -> styleRule

這里不關心它是怎么把tokens轉化成style的規則的,我們只要看格式化后的styleRule是怎么樣的就可以。每個styleRule主要包含兩個部分,一個是選擇器selectors,第二個是屬性集properties。用以下CSS:

打印出來的選擇器結果為(相關打印代碼省略):

selector  text    = “.text .hello”

                value = “hello”  matchType = “Class” relation = “Descendant”

                tag history selector text = “.text”

                value = “text”    matchType = “Class” relation = “SubSelector”

selector  text    = “#world”

                value = “world” matchType = “Id”      relation = “SubSelector”

從第一個選擇器可以看出,它的解析是從右往左的,這個在判斷match的時候比較有用。

blink定義了幾種matchType:

還定義了幾種選擇器的類型:

.text .hello的.hello選擇器的類型就是Descendant,即后代選擇器。記錄選擇器類型的作用是協助判斷當前元素是否match這個選擇器。例如,由于.hello是一個父代選器,所以它從右往左的下一個選擇器就是它的父選擇器,于是判斷當前元素的所有父元素是否匹配.text這個選擇器。

第二個部分——屬性打印出來是這樣的:

selector text = “.text .hello”

    perperty id = 15 value = “rgb(200, 200, 200)”

    perperty id = 316 value = “calc(100% – 20px)”

selector text = “#world”

    perperty id = 147 value = “20px”

    perperty id = 146 value = “20px”

    perperty id = 144 value = “20px”

    perperty id = 145 value = “20px”

所有的CSS的屬性都是用id標志的,上面的id依次對應:

設置了margin: 20px,會轉化成四個屬性。從這里可以看出CSS提倡屬性合并,但是最后還是會被拆成各個小屬性。所以屬性合并最大的作用應該在于減少CSS的代碼量。

 


鮮花
1

握手

雷人

路過

雞蛋

剛表態過的朋友 (1 人)

該文章已有0人參與評論

請發表評論

全部評論

老子是皇帝彩金