從css到頁面樣式渲染
寫了這麼多 class,color,background,display...
; 也許有時候會疑惑,怎麼就顯示在頁面上,改變元素的樣式。
本文簡明介紹整個解析,匹配,渲染過程
css 描述
css 是Cascading Style Sheets的簡寫,是一種樣式表語言。對應多種語法規則,可以為HTML 指定樣式。
基本規則
圖片來源 。
css 規則由兩部分組成: 選擇器
和 宣告
。上圖中 element
對應的 類選擇器
,緊接著 宣告(Declaration)
。
每條宣告由一個 屬性(Property)
加 冒號(:)
和 一個 值(value)
加 分號(;)
組成。
CSS 解析
解析 html 或外部css 文件,就是將文件轉化成為有意義的結構,能夠讓程式碼理解。解析結果代表了文件結構的節點樹,
解析分為詞法分析 和 語法分析。
詞法分析,也是編譯原理中的術語,從左到右一個字元一個字元的讀入源程式,對字元流進行掃描,根據構詞規則識別單詞,。這一過程可以使用lex等工具自動生成。
語法分析,主要任務是在詞法分析的基礎上,將單詞序列組合成各類語法短語,如“程式”, “語句”,“表示式”
解析工作通常會被拆分為兩個元件:
- 詞法分析器,負責將輸入流分解成有效的字元。
- 解析器,負責根據不同語言的語法規則來分析文件結構,最後構造出解析樹。
詞法分析器知道如何去除不相關的字元,比如空格和換行
具體到css解析,因為它是上下文無關的語法,可以利用各種解析器進行解析。webkit 使用Flex 和 Bison 解析器生成器,通過css 語法檔案自動建立解析器。解析器將CSS檔案解析成StyleSheet物件,且每個物件都包含CSS規則。CSS規則包含選擇器和宣告物件。
上圖是 一個畫素點的一生的ppt 中選出圖片, 感興趣可以看看演講視訊 ,非常直白,css樣式規則會被各種方式索引以便進行快速有效的查詢。實現各個樣式屬性的C++ 類,比如ppt中的BorderLeftColor類,是在構建時Python指令碼自動生成的。
具體到程式碼實現中,webkit 使用CSSRuleSet 物件來儲存style rule,在一般規則需要建立時,呼叫createStyleRule();
瀏覽器從右往左匹配選擇器
在使用css選擇器進行樣式匹配時,儘量少用層級關係,因為這樣可以減少選擇器匹配的次數,提高css 解析效率。
其實瀏覽器使用從右到左的解析順序,同樣提高了效率。通常在寫css 樣式時,我們一貫的想法是,從左往右解析,從根節點開始,一層一層遍歷匹配,直到所有的選擇器都匹配上了。瀏覽器解析的順序正好相反。
從右往左匹配的好處
簡單回顧下本次話題的上下文,也就是瀏覽器對頁面解析過程:
-
HTML parser
生成Dom Tree
-
css parser
生成style rules
,也就是CSSOM tree。 -
Dom
與CSSOM
匹配完成後,最後結合生成render tree
。 - 根據 render tree 開始 佈局
...
根據這張圖 ,可以對渲染過程大致瞭解。
回到瀏覽器匹配css 規則上,如果只有一個選擇器 對應匹配一個元素,從左往右匹配看似非常合理,但是正常情況是,一個dom節點,比如 <div class="cls1 cls2 cls3" id='id1'></div>
,可能對應了無數個css規則,沒有上限,我可以在上面加非常多的樣式。css 匹配效率的關鍵就是如何快速判斷儘可能多的選擇器並不能匹配。
先看看有多層巢狀的css規則。比如 #root .box .wrap i {}
, 如果從左往右解析,最左邊開始, 直到最右邊的選擇器i, dom節點上根本沒有 i
標籤, 遍歷到最後才排除css 規則。
相反,從最右邊的選擇器部分開始匹配,如果不成功,整個匹配過程就可以立刻結束;成功了,繼續往左,匹配父節點,跟樹的深度成正比。所以瀏覽器的匹配方式,可以非常快速的排除大部分的選擇器。
根據2009年在Firefox上做的測試 ,結論是僅僅從最右邊的選擇器開始檢查,就可以排除70%的規則。快速除去2/3的css規則後,後面只用擔心剩餘的1/3。
樣式作用在DOM元素上
從css文件被解析器解析完成,將資料儲存在物件模型中,獲取所有已解析的樣式規則,結合瀏覽器提供的預設樣式,計算出每個DOM 元素最終的樣式值。儲存在ComputedStyle物件模型中,它是由樣式屬性和值形成的map。
並且getComputedStyle 已經暴露出來,在js中通過 window.getComputedStyle
,可以獲取元素的最終樣式。
共享ComputedStyle
如果多個element的computedStyle不通過計算可以確認它們相等,那麼這些elements只會計算一次樣式,其餘的直接共享該ComputedStyle。
那些規則會共享computedStyle(待驗證):
- 該共享的element不能有id 屬性且CSS中還有該id的StyleRule,即使StyleRule 與 element 不匹配
- tagName 和 class 屬性必須一樣。
- mappedAttribute 必須相同。
- 不能使用sibling selector, 比如 :first-child, :last-child.
-
不能有style 屬性,哪怕style 屬性相同。他們也不會共享。
<p style="color:red">p1</p> <p style="color:red">p2</p>
渲染
頁面繪製到屏幕後,頁面結構的改變也有可能導致渲染樹重新計算,其中重排和重繪是最耗時的部分。
在頁面的生命週期中,隨時都有可能發生重排(Layout)和重繪(Painting)
重排(reflow)
當可見節點位置 及尺寸發生變化時都會發生重排,而且重排開銷比重繪更大。
至少會有一次重排,發生在初始化頁面佈局的時候。
觸發重排的幾種情況:
- 新增或刪除可見dom元素
- 元素位置改變
- 元素尺寸改變,
- 文字,內容,字型發生改變
- 頁面初始化渲染
....
重繪(repaints)
改變元素的外觀屬性(如background-color, border-color, visibility),不影響整個佈局,瀏覽器就會根據元素的新屬性重新繪製。
重繪不會帶來重新佈局
層合成(composite)
DOM樹中每個節點的都對應一個LayoutObject, 擁有相同的座標空間的LayoutObject,屬於同一渲染層(RenderLayers)。
渲染層保證元素按照正確順序合成(composite),正常展示元素的重疊以及元素透明等。
存在一些特殊情況,為滿足指定條件的LayoutObject會擁有獨立的渲染層,其他layoutobject則和第一個擁有渲染層的父元素公用一個。
利用chrome Devtools檢視繪製過程。
開啟Chrome Devtools,按下Esc建
在出現的面板上 點選左上角三點。
選擇rendering,轉到對應標籤。
開啟後,頁面中閃爍的綠色區域,表示這塊需要重新繪製。
當滾動側邊欄時,會出現整塊綠色
總結
本文對css整個渲染過程進行簡單介紹,試圖把大量複雜知識點串在一起,至少可以讓平時跟css打交道的我們,瞭解大致是個什麼過程,同時也是對自己學習過程中的一次總結。對於裡面涉及到的概念,都可以作為一個切入點,去好好研究。
參考連結
- Things nobody ever taught me about CSS
- 縮小樣式計算的範圍並降低其複雜性
- 為什麼瀏覽器讀取css規則的順序是從右到左
- why-do-browsers-match-css-selectors-from-right-to-left
- 【Hello CSS】第一章-CSS的語法與工作流
- 編譯原理之詞法分析、語法分析、語義分析
- How Browsers Work: Behind the scenes of modern web browsers
- 前端程式碼如何通過瀏覽器演化為螢幕顯示的畫素
- Webkit CSS引擎分析
- 簡化繪製的複雜度、減小繪製區域
- 無線效能優化:Composite