CSS 語法解析過程
1.在瀏覽器系列文章中,今天終點講下CSS解析這塊內容.我們已知瀏覽器的渲染流程中HTML Parser
會生成 DOM
樹,而 CSS Parser
會將解析結果附加到 DOM
樹上,如下圖:
解析分爲詞法分析 和 語法分析。
詞法分析
,也是編譯原理中的術語,從左到右一個字符一個字符的讀入源程序,對字符流進行掃描,根據構詞規則識別單詞。這一過程可以使用lex等工具自動生成。
語法分析
,主要任務是在詞法分析的基礎上,將單詞序列組合成各類語法短語,如“程序”, “語句”,“表達式”
解析工作通常會被拆分爲兩個組件:
- 詞法分析器,負責將輸入流分解成有效的字符。
- 解析器,負責根據不同語言的語法規則來分析文檔結構,最後構造出解析樹。
詞法分析器知道如何去除不相關的字符,比如空格和換行
具體到css解析,因爲它是上下文無關的語法,可以利用各種解析器進行解析。webkit 使用Flex 和 Bison 解析器生成器,通過css 語法文件自動創建解析器。解析器將CSS文件解析成StyleSheet對象,且每個對象都包含CSS規則。CSS規則包含選擇器和聲明對象。
2.CSS
有自己的規則,一般如下:
WebKit
使用 Flex
和 Bison
解析器生成器,通過 CSS
語法文件自動創建解析器。Bison
會創建自下而上的移位歸約解析器。Firefox
使用的是人工編寫的自上而下的解析器。
這兩種解析器都會將 CSS
文件解析成 StyleSheet
對象,且每個對象都包含 CSS
規則。CSS
規則對象則包含選擇器和聲明對象,以及其他與 CSS
語法對應的對象。
3.CSS
解析過程會按照 Rule
,Declaration
來操作:
4.那麼他是如何解析的呢,我們不妨打印一下 CSS Rules
:
控制檯輸入:
document.styleSheets[0].cssRules
打印出來的結果大致分爲幾類:
- cssText:存儲當前節點規則字符串
- parentRule:父節點的規則
- parentStyleSheet:包含 cssRules,ownerNode,rules 規則
- …
規則貌似有點看不懂,不用着急,我們接着往下看。
打印出來的結果大致分爲幾類:
5.CSS
解析和 Webkit
有什麼關係?
CSS 依賴 WebCore 來解析,而 WebCore 又是 Webkit 非常重要的一個模塊。
要了解 WebCore 是如何解析的,我們需要查看相關源碼:
CSSRule* CSSParser::createStyleRule(CSSSelector* selector)
{
CSSStyleRule* rule = 0;
if (selector) {
rule = new CSSStyleRule(styleElement);
m_parsedStyleObjects.append(rule);
rule->setSelector(sinkFloatingSelector(selector));
rule->setDeclaration(new CSSMutableStyleDeclaration(rule, parsedProperties, numParsedProperties));
}
clearProperties();
return rule;
}
從該函數的實現可以很清楚的看到,解析器達到某條件需要創建一個 CSSStyleRule
的時候將調用該函數,該函數的功能是創建一個 CSSStyleRule
,並將其添加已解析的樣式對象列表 m_parsedStyleObjects
中去,這裏的對象就是指的 Rule
。
注意:源碼是爲了參考理解,不需要逐行閱讀!
Webkit
使用了自動代碼生成工具生成了相應的代碼,也就是說詞法分析和語法分析這部分代碼是自動生成的,而 Webkit
中實現的 CallBack
函數就是在 CSSParser
中。
CSS 選擇器執行順序
渲染引擎解析 CSS
選擇器時是從右往左解析,這是爲什麼呢?舉個例子:
<div>
<div class="jartto">
<p><span> 111 </span></p>
<p><span> 222 </span></p>
<p><span> 333 </span></p>
<p><span class='yellow'> 444 </span></p>
</div>
</div>
<style>
div > div.jartto p span.yellow {
color: yellow;
}
</style>
我們按照「從左到右」的方式進行分析:
- 先找到所有
div
節點。 - 在
div
節點內找到所有的子div
,並且是class = “jartto”
。 - 然後再依次匹配
p span.yellow
等情況。 - 遇到不匹配的情況,就必須回溯到一開始搜索的
div
或者p
節點,然後去搜索下個節點,重複這樣的過程。
這樣的搜索過程對於一個只是匹配很少節點的選擇器來說,效率是極低的,因爲我們花費了大量的時間在回溯匹配不符合規則的節點。
我們按照「從右向左」的方式進行分析:
- 首先就查找到
class=“yellow”
的span
元素。 - 接着檢測父節點是否爲
p
元素,如果不是則進入同級其他節點的遍歷,如果是則繼續匹配父節點滿足class=“jartto”
的div
容器。 - 這樣就又減少了集合的元素,只有符合當前的子規則纔會匹配再上一條子規則。
綜上所述,我們可以得出結論:
瀏覽器 CSS 匹配核心算法的規則是以從右向左方式匹配節點的。
這樣做是爲了減少無效匹配次數,從而匹配快、性能更優。
所以,我們在書寫 CSS Selector
時,從右向左的 Selector Term
匹配節點越少越好。
不同 CSS
解析器對 CSS Rules
解析速度差異也很大,感興趣的童鞋可以看看 CSS 解析引擎,這裏不再贅述。
參考文檔:
https://segmentfault.com/a/1190000018759693
https://segmentfault.com/a/1190000021073560