<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【CSS】1340- CSS 渲染原理以及優(yōu)化策略

          共 8536字,需瀏覽 18分鐘

           ·

          2022-06-09 19:25

          提起 CSS 很多童鞋都很不屑,尤其是看到 RedMonk 2019 Programming Language Rankings 的時(shí)候,CSS 竟然排到了第七位。

          我們先來看看這張排行榜:

          css渲染原理

          既然 CSS 這么重要,那么我們花點(diǎn)時(shí)間來研究相關(guān)原理也就物有所值了。

          本節(jié)我們就來說說 CSS 渲染以及優(yōu)化相關(guān)的內(nèi)容,主要圍繞以下幾點(diǎn),由淺入深,了解來龍去脈:

          1. 瀏覽器構(gòu)成
          2. 渲染引擎
          3. CSS 特性
          4. CSS 語法解析過程
          5. CSS 選擇器執(zhí)行順序
          6. 高效的 ComputedStyle
          7. CSS 書寫順序?qū)π阅苡杏绊憜?/section>
          8. 優(yōu)化策略

          瀏覽器構(gòu)成

          瀏覽器構(gòu)成
          • User Interface:

            用戶界面,包括瀏覽器中可見的地址輸入框、瀏覽器前進(jìn)返回按鈕、書簽,歷史記錄等用戶可操作的功能選項(xiàng)。

          • Browser engine:

            瀏覽器引擎,可以在用戶界面和渲染引擎之間傳送指令或在客戶端本地緩存中讀寫數(shù)據(jù),是瀏覽器各個(gè)部分之間相互通信的核心。

          • Rendering engine:

            渲染引擎,解析 DOM 文檔和 CSS 規(guī)則并將內(nèi)容排版到瀏覽器中顯示有樣式的界面,也就是排版引擎,我們常說的瀏覽器內(nèi)核主要指的就是渲染引擎。

          • Networking:

            網(wǎng)絡(luò)功能模塊,是瀏覽器開啟網(wǎng)絡(luò)線程發(fā)送請求以及下載資源的模塊。

          • JavaScript Interpreter:

            JS 引擎,解釋和執(zhí)行 JS 腳本部分,例如 V8 引擎。

          • UI Backend:

            UI 后端則是用于繪制基本的瀏覽器窗口內(nèi)控件,比如組合選擇框、按鈕、輸入框等。

          • Data Persistence:

            數(shù)據(jù)持久化存儲(chǔ),涉及 Cookie、LocalStorage 等一些客戶端存儲(chǔ)技術(shù),可以通過瀏覽器引擎提供的 API 進(jìn)行調(diào)用。

          渲染引擎

          渲染引擎,解析 DOM 文檔和 CSS 規(guī)則并將內(nèi)容排版到瀏覽器中顯示有樣式的界面,也就是排版引擎,我們常說的瀏覽器內(nèi)核主要指的就是渲染引擎。

          渲染引擎

          上圖中,我們需要關(guān)注兩條主線:

          • 其一,HTML Parser 生成的 DOM 樹;
          • 其二,CSS Parser 生成的 Style Rules(CSSOM 樹);

          在這之后,DOM 樹與 Style Rules 會(huì)生成一個(gè)新的對(duì)象,也就是我們常說的 Render Tree 渲染樹,結(jié)合 Layout 繪制在屏幕上,從而展現(xiàn)出來。

          CSS 特性

          1.優(yōu)先級(jí)

          css優(yōu)先級(jí)
          選擇器權(quán)重
          !important1/0(無窮大)
          內(nèi)聯(lián)樣式1000
          ID100
          類/偽類/屬性10
          元素/偽元素1
          通配符/子選擇器/相鄰選擇器0
          !important > 行內(nèi)樣式(權(quán)重1000) > ID 選擇器(權(quán)重 100) > 類選擇器(權(quán)重 10) > 標(biāo)簽(權(quán)重1) > 通配符 > 繼承 > 瀏覽器默認(rèn)屬性

          示例代碼一:

          <div >
          <p id="box" class="text">Jartto's blogp>
          div>
          <style>
          #box{color: red;}
          .text{color: yellow;}
          style
          >

          猜一猜,文本會(huì)顯示什么顏色?當(dāng)你知道 「ID 選擇器 > 類選擇器 」的時(shí)候,答案不言自明。

          升級(jí)一下:

          <div id="box">
          <p class="text">Jartto's blogp>
          div>
          <style>
          #box{color: red;}
          .text{color: blue;}
          style
          >

          這里就考查到了規(guī)則「類選擇器 > 繼承」,ID 對(duì)文本來說是繼承過來的屬性,所以優(yōu)先級(jí)不如直接作用在元素上面的類選擇器。

          2.繼承性

          • 繼承得到的樣式的優(yōu)先級(jí)是最低的,在任何時(shí)候,只要元素本身有同屬性的樣式定義,就可以覆蓋掉繼承值。

          • 在存在多個(gè)繼承樣式時(shí),層級(jí)關(guān)系距離當(dāng)前元素最近的父級(jí)元素的繼承樣式,具有相對(duì)最高的優(yōu)先級(jí)。

          有哪些屬性是可以繼承的呢,我們簡單分一下類:

          1. font-family、font-size、font-weightf 開頭的 CSS 樣式。
          2. text-align、text-indentt 開頭的樣式。
          3. color。

          詳細(xì)的規(guī)則,請看下圖:

          css繼承性

          示例代碼二:

          <div>
          <ol>
          <li> Jartto's blog li>
          ol>
          div>

          <style>
          div { color : red!important; }
          ol { color : green; }
          style
          >

          增加了 !important,猜一猜,文本顯示什么顏色?

          3.層疊性

          css層疊性

          層疊就是瀏覽器對(duì)多個(gè)樣式來源進(jìn)行疊加,最終確定結(jié)果的過程。

          CSS 之所以有「層疊」的概念,是因?yàn)橛卸鄠€(gè)樣式來源。

          CSS 層疊性是指 CSS 樣式在針對(duì)同一元素配置同一屬性時(shí),依據(jù)層疊規(guī)則(權(quán)重)來處理沖突,選擇應(yīng)用權(quán)重高的 CSS 選擇器所指定的屬性,一般也被描述為權(quán)重高的覆蓋權(quán)重低的,因此也稱作層疊。

          示例代碼三:

          <div >
          <p class="two one">Jartto's blogp>
          div>
          <style>
          .one{color: red;}
          .two{color: blue;}
          <style>

          如果兩個(gè)類選擇器同時(shí)作用呢,究竟以誰為準(zhǔn)?這里我們要考慮樣式表中兩個(gè)類選擇器的先后順序,后面的會(huì)覆蓋前面的,所以文本當(dāng)然顯示藍(lán)色了。

          升級(jí)代碼:

          <div>
          <div>
          <div>Jartto's blogdiv>
          div>
          div>

          <style>
          div div div { color: green; }
          div div { color: red; }
          div { color: yellow; }
          <style>

          這個(gè)比較直接,算一下權(quán)重,誰大聽誰的。

          繼續(xù)升級(jí):

          <div id="box1" class="one">
          <div id="box2" class="two">
          <div id="box3" class="three"> Jartto's blog div>
          div>
          div>
          <style>
          .one .two div { color : red; }
          div #box3 { color : yellow; }
          #box1 div { color : blue; }
          style
          >

          權(quán)重:

          0 0 2 1
          0 1 0 1
          0 1 0 1

          驗(yàn)證一下:

          <div id="box1" class="one">
          <div id="box2" class="two">
          <div id="box3" class="three"> Jartto's blog div>
          div>
          div>
          <style>
          .one .two div { color : red; }
          #box1 div { color : blue; }
          div .three { color : green; }
          style
          >

          權(quán)重:

          0 0 2 1
          0 1 0 1
          0 0 1 1

          如果你對(duì)上面這些問題都了如指掌,那么恭喜你,基礎(chǔ)部分順利過關(guān),可以繼續(xù)升級(jí)了!

          CSS 語法解析過程

          1.我們來把 CSS 拎出來看一下,HTML Parser 會(huì)生成 DOM 樹,而 CSS Parser 會(huì)將解析結(jié)果附加到 DOM 樹上,如下圖:

          css

          2.CSS 有自己的規(guī)則,一般如下:WebKit 使用 FlexBison 解析器生成器,通過 CSS 語法文件自動(dòng)創(chuàng)建解析器。Bison 會(huì)創(chuàng)建自下而上的移位歸約解析器。Firefox 使用的是人工編寫的自上而下的解析器。

          這兩種解析器都會(huì)將 CSS 文件解析成 StyleSheet 對(duì)象,且每個(gè)對(duì)象都包含 CSS 規(guī)則。CSS 規(guī)則對(duì)象則包含選擇器和聲明對(duì)象,以及其他與 CSS 語法對(duì)應(yīng)的對(duì)象。

          css語法解析

          3.CSS 解析過程會(huì)按照 Rule,Declaration 來操作:

          css語法解析

          4.那么他是如何解析的呢,我們不妨打印一下 CSS Rules

          控制臺(tái)輸入:

          document.styleSheets[0].cssRules
          css語法解析

          打印出來的結(jié)果大致分為幾類:

          • cssText:存儲(chǔ)當(dāng)前節(jié)點(diǎn)規(guī)則字符串
          • parentRule:父節(jié)點(diǎn)的規(guī)則
          • parentStyleSheet:包含 cssRules,ownerNode,rules 規(guī)則

          規(guī)則貌似有點(diǎn)看不懂,不用著急,我們接著往下看。

          5.CSS 解析和 Webkit 有什么關(guān)系?

          css語法解析

          CSS 依賴 WebCore 來解析,而 WebCore 又是 Webkit 非常重要的一個(gè)模塊。

          要了解 WebCore 是如何解析的,我們需要查看相關(guān)源碼:

          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;
          }

          從該函數(shù)的實(shí)現(xiàn)可以很清楚的看到,解析器達(dá)到某條件需要?jiǎng)?chuàng)建一個(gè) CSSStyleRule 的時(shí)候?qū)⒄{(diào)用該函數(shù),該函數(shù)的功能是創(chuàng)建一個(gè) CSSStyleRule,并將其添加已解析的樣式對(duì)象列表 m_parsedStyleObjects 中去,這里的對(duì)象就是指的 Rule。

          注意:源碼是為了參考理解,不需要逐行閱讀!

          Webkit 使用了自動(dòng)代碼生成工具生成了相應(yīng)的代碼,也就是說詞法分析和語法分析這部分代碼是自動(dòng)生成的,而 Webkit 中實(shí)現(xiàn)的 CallBack 函數(shù)就是在 CSSParser 中。

          這時(shí)候就不得不提到 AST 了,我們繼續(xù)剖析。

          補(bǔ)充閱讀:Webkit 對(duì) CSS 支持

          6.關(guān)于 AST

          如果對(duì) AST 還不了解,請移步 AST 抽象語法樹。這里我們不做過多解釋,主要圍繞如何解析這一過程展開,先來看一張 Babel 轉(zhuǎn)換過程圖:

          ast

          我們來舉一個(gè)簡單的例子,聲明一個(gè)箭頭函數(shù),如下:

          let jarttoTest = () => {
          // Todo
          }

          通過在線編譯,生成如下結(jié)果:

          ast

          從上圖我們可以看出:我們的箭頭函數(shù)被解析成了一段標(biāo)準(zhǔn)代碼,包含了類型,起始位置,結(jié)束位置,變量聲明的類型,變量名,函數(shù)名,箭頭函數(shù)表達(dá)式等等。

          標(biāo)準(zhǔn)的解析代碼,我們可以對(duì)其進(jìn)行一些加工和處理,之后通過相應(yīng) API 輸出。

          很多場景都會(huì)用到這個(gè)過程,如:

          • JS 反編譯,語法解析。
          • Babel 編譯 ES6 語法。
          • 代碼高亮。
          • 關(guān)鍵字匹配。
          • 作用域判斷。
          • 代碼壓縮。

          場景千千萬,但是都離不開一個(gè)過程,那就是:

          AST 轉(zhuǎn)換過程:解析 - 轉(zhuǎn)換 - 生成

          到這里,CSS 如何解析的來龍去脈我們已經(jīng)非常清楚了,可以回到文章開頭的那個(gè)流程圖了,相信你一定會(huì)有另一翻感悟。

          CSS 選擇器執(zhí)行順序

          渲染引擎解析 CSS 選擇器時(shí)是從右往左解析,這是為什么呢?舉個(gè)例子:

          <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
          >

          我們按照「從左到右」的方式進(jìn)行分析:

          1. 先找到所有 div 節(jié)點(diǎn)。
          2. div 節(jié)點(diǎn)內(nèi)找到所有的子 div,并且是 class = “jartto”
          3. 然后再依次匹配 p span.yellow 等情況。
          4. 遇到不匹配的情況,就必須回溯到一開始搜索的 div 或者 p 節(jié)點(diǎn),然后去搜索下個(gè)節(jié)點(diǎn),重復(fù)這樣的過程。

          這樣的搜索過程對(duì)于一個(gè)只是匹配很少節(jié)點(diǎn)的選擇器來說,效率是極低的,因?yàn)槲覀兓ㄙM(fèi)了大量的時(shí)間在回溯匹配不符合規(guī)則的節(jié)點(diǎn)。

          我們按照「從右向左」的方式進(jìn)行分析:

          1. 首先就查找到 class=“yellow”span 元素。
          2. 接著檢測父節(jié)點(diǎn)是否為 p 元素,如果不是則進(jìn)入同級(jí)其他節(jié)點(diǎn)的遍歷,如果是則繼續(xù)匹配父節(jié)點(diǎn)滿足 class=“jartto”div 容器。
          3. 這樣就又減少了集合的元素,只有符合當(dāng)前的子規(guī)則才會(huì)匹配再上一條子規(guī)則。

          綜上所述,我們可以得出結(jié)論:

          瀏覽器 CSS 匹配核心算法的規(guī)則是以從右向左方式匹配節(jié)點(diǎn)的。

          這樣做是為了減少無效匹配次數(shù),從而匹配快、性能更優(yōu)。

          所以,我們在書寫 CSS Selector 時(shí),從右向左的 Selector Term 匹配節(jié)點(diǎn)越少越好。

          不同 CSS 解析器對(duì) CSS Rules 解析速度差異也很大,感興趣的童鞋可以看看 CSS 解析引擎,這里不再贅述。

          高效的 ComputedStyle

          瀏覽器還有一個(gè)非常棒的策略,在特定情況下,瀏覽器會(huì)共享 Computed Style,網(wǎng)頁中能共享的標(biāo)簽非常多,所以能極大的提升執(zhí)行效率!

          如果能共享,那就不需要執(zhí)行匹配算法了,執(zhí)行效率自然非常高。

          如果兩個(gè)或多個(gè) ElementComputedStyle 不通過計(jì)算可以確認(rèn)他們相等,那么這些 ComputedStyle 相等的 Elements 只會(huì)計(jì)算一次樣式,其余的僅僅共享該 ComputedStyle

          <section class="one">
          <p class="desc">Onep>
          section>

          <section class="one">
          <p class="desc">twop>
          section>

          如何高效共享 Computed Style ?

          1. TagNameClass 屬性必須一樣。
          2. 不能有 Style 屬性。哪怕 Style 屬性相等,他們也不共享。3.不能使用 Sibling selector,譬如: first-child:last-selector、 + selector。4.mappedAttribute 必須相等。

          為了更好的說明,我們再舉兩個(gè)例子:

          不能共享,上述規(guī)則 2

          <p style="color:red">jartto'sp>  
          <p style="color:red">blogp>

          可以共享,上述規(guī)則 4

          <p align="middle">jartto'sp>
          <p align="middle">blogp>

          到這里,相信你對(duì) ComputedStyle 有了更多的認(rèn)識(shí),代碼也就更加精煉和高效了。

          CSS 書寫順序?qū)π阅苡杏绊憜幔?/span>

          需要注意的是:瀏覽器并不是一獲取到 CSS 樣式就立馬開始解析,而是根據(jù) CSS 樣式的書寫順序?qū)⒅凑?DOM 樹的結(jié)構(gòu)分布渲染樣式,然后開始遍歷每個(gè)樹結(jié)點(diǎn)的 CSS 樣式進(jìn)行解析,此時(shí)的 CSS 樣式的遍歷順序完全是按照之前的書寫順序。

          在解析過程中,一旦瀏覽器發(fā)現(xiàn)某個(gè)元素的定位變化影響布局,則需要倒回去重新渲染。

          我們來看看下面這個(gè)代碼片段:

          width: 150px;
          height: 150px;
          font-size: 24px;
          position: absolute;

          當(dāng)瀏覽器解析到 position 的時(shí)候突然發(fā)現(xiàn)該元素是絕對(duì)定位元素需要脫離文檔流,而之前卻是按照普通元素進(jìn)行解析的,所以不得不重新渲染。

          渲染引擎首先解除該元素在文檔中所占位置,這就導(dǎo)致了該元素的占位情況發(fā)生了變化,其他元素可能會(huì)受到它回流的影響而重新排位。

          我們對(duì)代碼進(jìn)行調(diào)整:

          position: absolute;
          width: 150px;
          height: 150px;
          font-size: 24px;

          這樣就能讓渲染引擎更高效的工作,可是問題來了:

          在實(shí)際開發(fā)過程中,我們?nèi)绾文鼙WC自己的書寫順序是最優(yōu)呢?

          這里有一個(gè)規(guī)范,建議順序大致如下:

          1. 定位屬性

            position  display  float  left  top  right  bottom   overflow  clear   z-index
          2. 自身屬性

            width  height  padding  border  margin   background
          3. 文字樣式

            font-family   font-size   font-style   font-weight   font-varient   color
          4. 文本屬性

            text-align   vertical-align   text-wrap   text-transform   text-indent    text-decoration   letter-spacing    word-spacing    white-space   text-overflow
          5. CSS3 中新增屬性

            content   box-shadow   border-radius  transform

          當(dāng)然,我們需要知道這個(gè)規(guī)則就夠了,剩下的可以交給一些插件去做,譬如 CSSLint(能用代碼實(shí)現(xiàn)的,千萬不要去浪費(fèi)人力)。

          優(yōu)化策略

          我們從瀏覽器構(gòu)成,聊到了渲染引擎,再到 CSS 的解析原理,最后到執(zhí)行順序,做了一系列的探索。期望大家能從 CSS 的渲染原理中了解整個(gè)過程,從而寫出更高效的代碼。

          1. 使用 id selector 非常的高效

          在使用 id selector 的時(shí)候需要注意一點(diǎn):因?yàn)?id 是唯一的,所以不需要既指定 id 又指定 tagName

          /* Bad  */
          p#id1 {color:red;}

          /* Good */
          #id1 {color:red;}

          2. 避免深層次的 node

          譬如:

          /* Bad  */
          div > div > div > p {color:red;}
          /* Good */
          p-class{color:red;}

          3. 不要使用 attribute selector

          如:p[att1=”val1”],這樣的匹配非常慢。更不要這樣寫:p[id="id1"],這樣將 id selector 退化成 attribute selector

          /* Bad  */
          p[id="jartto"]{color:red;}
          p[class="blog"]{color:red;}
          /* Good */
          #jartto{color:red;}
          .blog{color:red;}

          4. 將瀏覽器前綴置于前面,將標(biāo)準(zhǔn)樣式屬性置于最后

          類似:

          .foo {
          -moz-border-radius: 5px;
          border-radius: 5px;
          }

          可以參考這個(gè) Css 規(guī)范。

          5. 遵守 CSSLint 規(guī)則

          font-faces                 不能使用超過5個(gè)web字體
          import            禁止使用@import
          regex-selectors      禁止使用屬性選擇器中的正則表達(dá)式選擇器
          universal-selector       禁止使用通用選擇器*
          unqualified-attributes    禁止使用不規(guī)范的屬性選擇器
          zero-units       0后面不要加單位
          overqualified-elements    使用相鄰選擇器時(shí),不要使用不必要的選擇器
          shorthand          簡寫樣式屬性
          duplicate-background-images 相同的url在樣式表中不超過一次

          6. 減少 CSS 文檔體積

          • 移除空的 CSS 規(guī)則(Remove empty rules)。
          • 值為 0 不需要單位。
          • 使用縮寫。
          • 屬性值為浮動(dòng)小數(shù) 0.xx,可以省略小數(shù)點(diǎn)之前的 0
          • 不給 h1-h6 元素定義過多的樣式。

          7. CSS Will Change

          WillChange 屬性,允許作者提前告知瀏覽器的默認(rèn)樣式,使用一個(gè)專用的屬性來通知瀏覽器留意接下來的變化,從而優(yōu)化和分配內(nèi)存。

          8. 不要使用 @import

          使用 @import 引入 CSS 會(huì)影響瀏覽器的并行下載。

          使用 @import 引用的 CSS 文件只有在引用它的那個(gè) CSS 文件被下載、解析之后,瀏覽器才會(huì)知道還有另外一個(gè) CSS 需要下載,這時(shí)才去下載,然后下載后開始解析、構(gòu)建 Render Tree 等一系列操作。

          多個(gè) @import 會(huì)導(dǎo)致下載順序紊亂。在 IE 中,@import 會(huì)引發(fā)資源文件的下載順序被打亂,即排列在 @import 后面的 JS 文件先于 @import 下載,并且打亂甚至破壞 @import 自身的并行下載。

          9. 避免過分回流/重排(Reflow

          瀏覽器重新計(jì)算布局位置與大小。

          常見的重排元素:

          width
          height
          padding
          margin
          display
          border-width
          border
          top
          position
          font-size
          float
          text-align
          overflow-y
          font-weight
          overflow
          left
          font-family
          line-height
          vertical-align
          right
          clear
          white-space
          bottom
          min-height

          10. 高效利用 computedStyle

          • 公共類。
          • 慎用 ChildSelector。
          • 盡可能共享。

          更多請查看上文 - 高效的 ComputedStyle

          11. 減少昂貴屬性

          當(dāng)頁面發(fā)生重繪時(shí),它們會(huì)降低瀏覽器的渲染性能。所以在編寫 CSS 時(shí),我們應(yīng)該盡量減少使用昂貴屬性,如:

          • box-shadow
          • border-radius。
          • filter
          • :nth-child。

          12. 依賴?yán)^承

          如果某些屬性可以繼承,那么自然沒有必要在寫一遍。

          13. 遵守 CSS 順序規(guī)則

          上面就是對(duì)本文的一個(gè)總結(jié),你了解 CSS 具體的實(shí)現(xiàn)原理,曉得規(guī)避錯(cuò)誤書寫方式,知道為什么這么優(yōu)化,這就夠了。

          性能優(yōu)化,進(jìn)無止境。

          文章首發(fā)于 Jartto's blog

          轉(zhuǎn)載自:http://jartto.wang/2019/10/23/css-theory-and-optimization/

          作者:Jartto


          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色噜噜日韩精品欧美一区二区 | 99丝袜视频在线观看 | 成人欧美在线观看 | 丁香五月天婷婷婷 | 港台靓女性啪啪天美传媒精品性88xo |