摘要:書接上文瀏覽器內(nèi)核之解釋器和模型本文剖析的解釋器和樣式布局。根據(jù)生成解釋器類。而后將解釋后的信息設(shè)置到元素的屬性的樣式中,然后設(shè)置標記表明該元素需要重新計算樣式,并觸發(fā)重新計算布局。
微信公眾號:愛寫bugger的阿拉斯加前言
如有問題或建議,請后臺留言,我會盡力解決你的問題。
此文章是我最近在看的【W(wǎng)ebKit 技術(shù)內(nèi)幕】一書的一些理解和做的筆記。
而【W(wǎng)ebKit 技術(shù)內(nèi)幕】是基于 WebKit 的 Chromium 項目的講解。
書接上文 瀏覽器內(nèi)核之 HTML 解釋器和 DOM 模型
本文剖析 WebKit 的 CSS 解釋器和樣式布局。
從整個網(wǎng)頁的加載和渲染過程來看,CSS 解釋器和規(guī)則匹配處于 DOM 樹建立之后,RenderObject 樹建立之前,CSS 解釋器解釋后的結(jié)果會保存起來,然后 RenderObject 樹基于該結(jié)果來進行規(guī)范匹配和布局計算。當網(wǎng)頁有用戶交互或者動畫等動作的時候,通過 CSSDOM 等技術(shù),JavaScript 代碼同樣可以非常方便地修改 CSS 代碼,WebKit 此時需要重新解釋樣式并重復以上這一過程。
1. CSS 基本功能 1.1.1 簡介CSS 的全稱是 Cascading Style Sheet,中文名是級聯(lián)樣式表,主要用來控制網(wǎng)頁的顯示風格。
1.1.2 樣式規(guī)則圖 6-1 描述了一個典型的 CSS 規(guī)則結(jié)構(gòu)。一個規(guī)則包括兩個部分——規(guī)則關(guān)和規(guī)則體。規(guī)則頭由一個或者多個選擇器組成;規(guī)則體則由一個或者多個樣式聲明組成,每個樣式聲明由樣式名和樣式值構(gòu)成,表示這個規(guī)則對哪些樣式進行了規(guī)定和設(shè)置。
當 HTML 中的某個元素經(jīng)過后面的匹配算法使用了這條規(guī)則,那么將這些樣式設(shè)置成該元素的樣式,除非有更高優(yōu)先級的規(guī)則匹配上該元素。
1.1.3 選擇器CSS 的選擇器是一級模式,用來匹配相應(yīng)的 HTML 元素。當選擇器匹配相應(yīng)元素的時候,該選擇器包含的各種樣式值就會作用于匹配的元素上。通過選擇器,CSS 能夠精準地控制 HTML 頁面中的任意一個或者多個元素的樣式屬性。
具體的,這里不做介紹,請查閱 CSS 規(guī)范。
1.1.4 框模型框模型(Box model,或稱箱子模型)就是我們常說的盒子模型,是CSS 標準中引入來表示 HTML 標簽元素的布局結(jié)構(gòu)。一個框模型大致包括四個部分:外邊距(Margin)、邊框(Border)、內(nèi)邊距(Padding)和內(nèi)容(Content)。
1.4.5 包含塊(Containing Block)模型當 WebKit 計算元素的箱子的位置和大小時,WebKit 需要計算該元素和另外一個矩形區(qū)域的相對位置,這個矩形區(qū)域稱為該元素的包含塊。上面介紹的框模型就是在包含塊內(nèi)計算和確定各個元素的,包含塊的具體定義如下:
根元素的包含塊稱為初始包含塊,通常它的大小就是可視區(qū)域(Viewport)的大小。
對于其他位置屬性設(shè)置為 “static” 或者 “relative” 的元素,它的包含塊就是最近祖先的箱子模型中的內(nèi)容區(qū)域(Content)。
如果元素的位置屬性為 “fixed” ,那么該元素的包含快脫離 HTML 文檔,因定在可視區(qū)域的某個特定位置。
如果元素的位置屬性為 “absolute” ,那么該元素的包含塊由最近的含有屬性 “absolute”、“relative”、或者 “fixed” 的祖先決定,具體規(guī)則如下:如果一個元素具有 “inline” 屬性,那么元素的包含塊是該祖先的第一個和最近一個 inline 框的內(nèi)邊距的區(qū)域;否則,包含塊則是該祖先的內(nèi)邊距所包圍的區(qū)域。
1.1.6 CSS 樣式屬性CSS 標準中定義了各式各樣的樣式屬性,用來描述元素的顯示效果。
這些屬性大致分成以下類型:
背景:如背景顏色和背景圖片等。
文本:設(shè)置文本縮進,對齊。單詞間隔。字母間隔。字符轉(zhuǎn)換、裝飾和空白字符等。
字體:設(shè)置字體屬性,可以是內(nèi)嵌的,也可以是自定義字體的方式,另外還可以設(shè)置加粗、變形等屬性。
列表:設(shè)置列表類型,可以以字母、希臘字母、數(shù)字等方式編號列表。
表格:通過設(shè)置邊框來達到顯示表格的視覺效果的目的。設(shè)置是否把表格邊框合并為單一的邊框,設(shè)置分隔單元格邊框的距離,設(shè)置表格標題的位置,設(shè)置是否顯示表格中的空單元格,設(shè)置顯示單元、行和列的算法等。
定位:CSS 提供元素的相對、絕對定位和浮動定位。
1.1.7 CSSOM(CSS Object Model)CSSOM 稱為 CSS 對象模型。它思想是在 DOM 中的一些節(jié)點接口中,加入獲取和操作 CSS 屬性或者接口的 JavaScript 接口,因而 JavaScript 可以動態(tài)操作 CSS 樣式。DOM 提供了接口讓 JavaScript 修改 HTML 文檔,同理,CSSOM 提供了接口讓 JavaScript 獲得和修改 CSS 代碼設(shè)置的樣式信息。
對于內(nèi)部和外部樣式表,CSSOM 定義了樣式表的接口,稱為 “CSSStyleSheet”, 這是一個可以在 JavaScript 代碼中訪問的接口。借助這個接口,開發(fā)者可以在 JavaScript 中獲取樣式表的各種信息,例如 CSS 的 “href”、樣式表類型 “type”、規(guī)則信息 “cssRules” 等,甚至可以獲取樣式表中的 CSS 規(guī)則列表。這個接口同 DOM 中的 “Script” 節(jié)點或者 “Link” 節(jié)點不一樣,它是 CSSOM 定義的新接口。開發(fā)者可以通過 document.styleSheets 查看當前網(wǎng)頁中包含的所有 CSS 樣式表,這是因為 CSSOM 對 DOM 中的 Document 接口進行了擴展,下面是新加入的屬性:
W3C 還定義了另外一個規(guī)范是 CSSDOM View,它的基本含義是增加一些新的屬性到 Window、Document、Element、HTMLElement 和 MouseEvent 等接口,這些 CSS 的屬性能夠讓 JavaScript 獲取視圖信息,用于表示跟視圖相關(guān)的特征,例如窗口大小,網(wǎng)頁滾動位移,元素的框位置、鼠標事件的坐標等信息。下面是以 CSSDOM View 對 Window 的擴展:
1.2 CSS 解釋器和規(guī)則匹配 1.2.1 樣式的 WebKit 表示類對于 CSS 樣式表,不管是內(nèi)嵌還是外部文檔,WebKit 都使用 CSSStyleSheet 類來表示。圖 6-5 描述了 WebKit 內(nèi)部是如何表示 CSS 文檔的。
一切的起源都是從 DOM 的 Document 類開始。
DoucmentStyleSheetCollection 類,該類包含了所在 CSS 樣式表
WebKit的內(nèi)部表示類 CSSStyleSheet,它包含 CSS 的 href 、類型、內(nèi)容等信息。
CSS 的內(nèi)容就是樣式信息 StyleSheetContent,包含了一個樣式規(guī)則 (StyleRuleBase)列表。樣式規(guī)則被 用在 CSS 的解釋器的工作過程中。
下面部分 WebKit 主要是將解釋之后的規(guī)則組織起來,用于為 DOM 中的元素匹配相應(yīng)的規(guī)則,從而應(yīng)用規(guī)則中的屬性值序列。這一過程的主要負責者是 StyleSheetResolver 類,它屬于 Document 類,并包含了一個 DocumentRuleSets 類用來表示多個規(guī)則集合(RuleSet)。每個規(guī)則集合就是將之前解釋之后的結(jié)果合并起來,并進行分類,例如 id 類規(guī)則,標簽類規(guī)則等。至于為什么是多個規(guī)則集合,是因為這些規(guī)則集合可能源自于默認的規(guī)則集合,或者網(wǎng)頁自定義的規(guī)則集合等。
1.2.2 解釋過程CSS 解釋過程是指從 CSS 字符串經(jīng)過 CSS 解釋器處理后變成渲染引擎內(nèi)部規(guī)則的表示過程。
在 WebKit 中,過程如 6-8 所示。
這一過程是基本思想是由 CSSParser 類負責。CSSParser 類其實也是橋接類,實際的解釋工作是由 CSSGrammer.y.in 來完成。CSSGrammer.y.in 是Bison 的輸入文件,Bioson 是一個生成解釋器的工具。Bison 根據(jù) CSSGrammer.y.in 生成 CSS 解釋器——CSSGrammer 類。當然 CSSGrammer 類需要調(diào)用 CSSParser類來處理解釋結(jié)果,例如需要使用 CSSParser 類創(chuàng)建選擇器對象、屬性、規(guī)則等。
在解釋網(wǎng)頁中自定義的 CSS 樣式之前,實際上 WebKit 渲染引擎會為每個網(wǎng)頁設(shè)置一個默認的樣式,這決定了網(wǎng)頁所沒有設(shè)置的元素屬性及其屬性默認值和將要顯示的效果。一般來講,不同的 WebKit 移植可以設(shè)置不同的默認樣式。下面是 Chrome 瀏覽器使用的默認樣式,這些樣式?jīng)Q定了默認的網(wǎng)頁顯示效果。
1.2.4 樣式規(guī)則匹配樣式規(guī)則建立完成之后,WebKit 保存規(guī)則結(jié)果在 DocumentRuleSets 對象類中。當 DOM 的節(jié)點建立之后,WebKit 會為其中的一些節(jié)點(只限于可視節(jié)點)選擇合適的樣式信息。這些工作都是由 StyleResolver 來負責。當然,實際的匹配工作還是在 DocumentRuleSets 類中完成的。
圖 6-9 描述了參與樣式規(guī)則匹配的 WebKit 主要相關(guān)類?;舅悸肥鞘褂?StyleResolver 類來為 DOM 的元素節(jié)點匹配樣式。StyleResolver 類根據(jù)元素的信息,例如標簽名、類別等,從樣式規(guī)則中查找最匹配的規(guī)則,然后將樣式信息保存到新建的 RenderStyle 對象中。最后,最后這些 RenderStyle 對象被 RenderObject 類所管理和使用。
規(guī)則的匹配則是由 ElementRuleCollector 類來計算并獲得,它根據(jù)元素的屬性等,并從 DocumentRuleSets 類中獲取規(guī)則集合,依次按照 ID、類別、標簽等選擇器信息逐次匹配獲得元素的樣式。
首先,當 WebKit 需要為 HTML 元素創(chuàng)建 RenderObject 類的時候,首先 StyleResolver 類負責獲取樣式信息,并返回 RenderStyle 對象,RenderStyle 對象包含了匹配完的結(jié)果樣式結(jié)果。
其次,根據(jù)實際需求,每個元素可能需要匹配不同來源的規(guī)則,依次是用戶代理(瀏覽器)規(guī)則集合、用戶規(guī)則集合和 HTML 網(wǎng)頁中包含的自定義規(guī)則集合。這三個規(guī)則的匹配方式是類似的。這里是以自定義規(guī)則匹配為例加以說明的。
再次,對于自定義規(guī)則集合,它先查找 ID 規(guī)則,檢查有無匹配的規(guī)則,之后依次檢查類型規(guī)則,標簽規(guī)則等,如果某個規(guī)則匹配上該元素,WebKit 把這些規(guī)則保存到匹配結(jié)果中。
最后,WebKit 對這些規(guī)則進行排序。對于該元素需要的樣式屬性,WebKit 選擇從高優(yōu)先級規(guī)則中選取,并將樣式屬性值返回。
1.2.5 JavaScript 設(shè)置樣式CSSDOM 定義了 JavaScript 訪問樣式的能力和方式。使用 CSSDOM 接口來更改屬性值的過程,在 WebKit 中,這需要 JavaScript 引擎和渲染引擎協(xié)同完成。
大致的過程是,JavaScript 引擎調(diào)用設(shè)置屬性值的公共處理函數(shù),然后該函數(shù)調(diào)用屬性值解析函數(shù),在這個例子中則是 CSS 的 JavaScript 綁定函數(shù)。而后 WebKit 將解釋后的信息設(shè)置到元素的 “style” 屬性的樣式 “webkitTransform” 中,然后設(shè)置標記表明該元素需要重新計算樣式,并觸發(fā)重新計算布局。最后是 WebKit 的重新繪圖,圖 6-12 描述了其中的主要過程。
1.3 WebKit 布局 1.3.1 基礎(chǔ)當 WebKit 創(chuàng)建 RenderObject 對象之后,每個對象是不知道自己的位置、大小等信息的,WebKit 根據(jù)框模型來計算它們的位置,大小等信息的過程稱為布局計算。
第五章描述過 Frame 類,用于表示網(wǎng)頁的框結(jié)構(gòu),每個框都有一個 FrameView 類,用于表示框的視圖結(jié)構(gòu)。
FrameView 類主要負責視圖方面的任務(wù),例如網(wǎng)頁視圖大小,滾動、布局計算、繪圖等,它是一個總?cè)肟陬悺?/strong> “l(fā)ayout” 和 “needsLayout” ,它們用來布局計算和決定是否需要布局計算,實際的布局計算則是在 RenderObject 類中。
布局計算根據(jù)其計算的范圍大致可以分為兩類:第一類是對整個 RenderObject 樹進行的計算;第二類是對 RenderObject 樹中某個子樹的計算,常見于文本元素或者是 overflow:auto 塊的計算,這種情況一般是其子樹布局的改變不會影響其周圍元素的布局,因而不需要重新計算更大范圍內(nèi)的布局。
1.3.2 布局計算布局計算是一個遞歸的過程,因為一個節(jié)點的大小通常需要先計算它的子女節(jié)點的位置,大小等信息。
圖 6-14 描述了 RenderObject 節(jié)點計算布局的主要過程,中間省略了很多判斷和步驟,主要邏輯都是由 RenderObject 類的 “l(fā)ayout” 函數(shù)來完成。
首先,該函數(shù)會判斷 RenderObject 節(jié)點是否需要重新計算,通常這需要通過檢查位數(shù)組中的相應(yīng)標記位、子女是否需要計算布局等來確定。
其次,該函數(shù)會確定網(wǎng)頁的寬度和垂直方向上的外邊距,這是因為網(wǎng)頁通常是垂直方向上滾動,而水平方向盡量不需要滾動。
再次,該函數(shù)會遍歷其每一個子女節(jié)點,依次計算它們的布局。每一個元素會實現(xiàn)自己的 “l(fā)ayout” 函數(shù),根據(jù)特定的算法來計算該類型元素的布局。如果頁面元素定義了自身的寬高,那么 WebKit 按照定義的寬高來確定元素的大小,而對于像文本節(jié)點這樣的內(nèi)聯(lián)元素則需要結(jié)合其字號大小及文字的多少等來確定其對應(yīng)的寬高。如果頁面元素所確定的寬高超過了布局容器包含塊所能提供的寬高,同時其 overflow 的屬性為 visible 或 auto , WebKit 則會提供滾動條來保證可以顯示其所有內(nèi)容。除非網(wǎng)頁定義了頁面元素的寬高,一般來說頁面元素的寬高是在布局的時候通過相關(guān)計算得出來的。如果元素它有子女,則 WebKit 需要遞歸這一過程。
最后,節(jié)點根據(jù)它的子女們的大小計算得出自己的高度,整個過程結(jié)束。
重新布局的情況:
首先,當網(wǎng)頁首次被打開的時候,瀏覽器設(shè)置網(wǎng)頁的可視區(qū)域(viewport),并調(diào)用計算布局的方法。這其實也描述了一種常見的情景,就是當可視區(qū)域發(fā)生變化的時候,WebKit 都需要重新計算布局,這是因為網(wǎng)頁的包含塊的大小發(fā)生了改變。
其次,網(wǎng)頁的動畫會觸發(fā)布局計算。當網(wǎng)頁顯示結(jié)束后,動畫可能改變樣式屬性,那么 WebKit 就需要重新計算。
然后,JavaScript 代碼通過 CSSDOM 等直接修改樣式信息,它們也會觸發(fā) WebKit 重新計算布局。
最后,用戶的交互也會觸發(fā)布局計算,例如翻滾網(wǎng)頁,這會角觸發(fā)新區(qū)域布局計算。
CSS 的布局計算是以包含塊和框模型為基礎(chǔ)的,這表示這些元素的布局計算都依賴于塊,例如 “div” 通常就是一個塊,如前面所述它們通常是在垂直方向上展開。
但是,CSS 標準也規(guī)定了行布局形式,這就是內(nèi)聯(lián)元素。內(nèi)聯(lián)元素表現(xiàn)的是行布局形式,就是說這些元素以行進行顯示。
以 “div” 元素為例,如果設(shè)置屬性 “style” 為 “displa: inline” 時,則該元素是內(nèi)聯(lián)元素,那么它可能與前面的元素在同一行。如果該元素沒有設(shè)置這個屬性時,則是塊元素,那么在新的行里顯示。這顯然會增加處理的復雜性,為此,WebKit 的處理方式是 ——對于一個塊元素對應(yīng)的 RenderObject 對象,它的子女要么都是塊元素的 RenderObject 對象,要么都是非內(nèi)聯(lián)元素對應(yīng)的 RenderObject 對象,這可以通過建立匿名塊(Anonymous Block)對象來實現(xiàn)。
布局計算相對也是比較耗時間的,更糟糕的是,一旦布局發(fā)生變化,WebKit 就需要后面的重新繪制操作。另一方面,減少樣式的變動而依賴現(xiàn)在 HTML5 的新功能可以有效地提高網(wǎng)頁的渲染效率。
總結(jié)匹配算法結(jié)合 CSS 規(guī)則來設(shè)置樣式
選擇器就是選中某個元素的
框模型就是常說的盒子模型,包含 margin、border、padding、content
CSSOM 稱為 CSS 對象模型,JavaScript 可以獲取和操作 CSS 屬性。
CSS 解釋過程是指從 CSS 字符串經(jīng)過 CSS 解釋器處理后變成渲染引擎內(nèi)部規(guī)則的表示過程。
當 WebKit 創(chuàng)建 RenderObject 對象之后,每個對象是不知道自己的位置、大小等信息的,WebKit 根據(jù)框模型(Frame 類的 FrameView)來計算它們的位置,大小等信息的過程稱為布局計算
布局計算是一個遞歸的過程,而且還會發(fā)生重新布局。
最后希望本文對你有點幫助。
下期分享 第七章 渲染基礎(chǔ) 敬請期待。
對 全棧開發(fā) 有興趣的朋友可以掃下方二維碼關(guān)注我的公眾號 —— 愛寫bugger的阿拉斯加
分享 web 開發(fā)相關(guān)的技術(shù)文章,熱點資源,全棧程序員的成長之路。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/113903.html
摘要:書接上文瀏覽器內(nèi)核之解釋器和模型本文剖析的解釋器和樣式布局。根據(jù)生成解釋器類。而后將解釋后的信息設(shè)置到元素的屬性的樣式中,然后設(shè)置標記表明該元素需要重新計算樣式,并觸發(fā)重新計算布局。 showImg(https://segmentfault.com/img/remote/1460000016215814); 微信公眾號:愛寫bugger的阿拉斯加如有問題或建議,請后臺留言,我會盡力解決...
摘要:響應(yīng)由三個部分組成,分別是狀態(tài)行消息報頭響應(yīng)正文。詳情參考小汪之前寫的文章瀏覽器內(nèi)核之解釋器和模型解釋解釋過程是指從字符串經(jīng)過解釋器處理后變成渲染引擎內(nèi)部規(guī)則的表示過程。 showImg(https://segmentfault.com/img/remote/1460000016404846); 前言 小汪最近在看【W(wǎng)ebKit 技術(shù)內(nèi)幕】一書,說實話,這本書寫的太官方了,不通俗易懂。...
摘要:文章同步到技術(shù)內(nèi)幕之頁面渲染過程最近拜讀了傳說中的技術(shù)內(nèi)幕一書,有很大收獲,尤其是對頁面渲染有了較深的認識。解析語法分析,基于詞法解釋器生成的新標記,構(gòu)建成抽象語法樹,解析器嘗試將其與某條語法規(guī)則進行匹配。 文章同步到github《Webkit技術(shù)內(nèi)幕》之頁面渲染過程 最近拜讀了傳說中的《Webkit技術(shù)內(nèi)幕》一書,有很大收獲,尤其是對頁面渲染有了較深的認識。由于功力有限,而且書中設(shè)...
摘要:文章同步到技術(shù)內(nèi)幕之頁面渲染過程最近拜讀了傳說中的技術(shù)內(nèi)幕一書,有很大收獲,尤其是對頁面渲染有了較深的認識。解析語法分析,基于詞法解釋器生成的新標記,構(gòu)建成抽象語法樹,解析器嘗試將其與某條語法規(guī)則進行匹配。 文章同步到github《Webkit技術(shù)內(nèi)幕》之頁面渲染過程 最近拜讀了傳說中的《Webkit技術(shù)內(nèi)幕》一書,有很大收獲,尤其是對頁面渲染有了較深的認識。由于功力有限,而且書中設(shè)...
摘要:文章同步到技術(shù)內(nèi)幕之頁面渲染過程最近拜讀了傳說中的技術(shù)內(nèi)幕一書,有很大收獲,尤其是對頁面渲染有了較深的認識。解析語法分析,基于詞法解釋器生成的新標記,構(gòu)建成抽象語法樹,解析器嘗試將其與某條語法規(guī)則進行匹配。 文章同步到github《Webkit技術(shù)內(nèi)幕》之頁面渲染過程 最近拜讀了傳說中的《Webkit技術(shù)內(nèi)幕》一書,有很大收獲,尤其是對頁面渲染有了較深的認識。由于功力有限,而且書中設(shè)...
閱讀 3430·2021-11-12 10:36
閱讀 2745·2021-11-11 16:55
閱讀 2970·2021-09-27 13:36
閱讀 1620·2021-08-05 10:01
閱讀 3560·2019-08-30 15:55
閱讀 774·2019-08-30 13:01
閱讀 1914·2019-08-29 17:16
閱讀 2383·2019-08-29 16:40