沐鳴平台首頁_如何提升 CSS 選擇器的性能?

 

CSS選擇器對性能的影響源於瀏覽器匹配選擇器和文檔元素時所消耗的時間,所以優化選擇器的原則是應盡量避免使用消耗更多匹配時間的選擇器。而在這之前我們需要了解CSS選擇器匹配的機制, 如子選擇器規則:

#header > a {font-weight:blod;}

我們中的大多數人都是從左到右的閱讀習慣,會習慣性的設定瀏覽器也是從左到右的方式進行匹配規則,推測這條規則的開銷並不高。

我們會假設瀏覽器以這樣的方式工作:尋找 id 為 header 的元素,然後將樣式規則應用到直系子元素中的 a 元素上。我們知道文檔中只有一個 id 為 header 的元素,並且它只有幾個 a 元素的子節點,所以這個CSS選擇器應該相當高效。

事實上,卻恰恰相反,CSS選擇器是從右到左進行規則匹配。了解這個機制后,例子中看似高效的選擇器在實際中的匹配開銷是很高的,瀏覽器必須遍歷頁面中所有的 a 元素並且確定其父元素的 id 是否為 header 。

如果把例子的子選擇器改為後代選擇器則會開銷更多,在遍歷頁面中所有 a 元素后還需向其上級遍歷直到根節點。

#header  a {font-weight:blod;}

理解了CSS選擇器從右到左匹配的機制后,明白只要當前選擇符的左邊還有其他選擇符,樣式系統就會繼續向左移動,直到找到和規則匹配的選擇符,或者因為不匹配而退出。我們把最右邊選擇符稱之為關鍵選擇器

如何減少 CSS 選擇器性能損耗?

Google 資深web開發工程師 Steve Souders 對 CSS 選擇器的執行效率從高到低做了一個排序:

1.id選擇器(#myid)
2.類選擇器(.myclassname)
3.標籤選擇器(div,h1,p)
4.相鄰選擇器(h1+p)
5.子選擇器(ul < li)
6.後代選擇器(li a)
7.通配符選擇器(*)
8.屬性選擇器(a[rel=”external”])
9.偽類選擇器(a:hover, li:nth-child)

根據以上「選擇器匹配」與「選擇器執行效率」原則,我們可以通過避免不恰當的使用,提升 CSS 選擇器性能。

1、避免使用通用選擇器

.content * {color: red;}

瀏覽器匹配文檔中所有的元素後分別向上逐級匹配 class 為 content 的元素,直到文檔的根節點。因此其匹配開銷是非常大的,所以應避免使用關鍵選擇器是通配選擇器的情況。

2、避免使用標籤或 class 選擇器限制 id 選擇器

BAD
button#backButton {…}
BAD
.menu-left#newMenuIcon {…}
GOOD
#backButton {…}
GOOD
#newMenuIcon {…}

3、避免使用標籤限制 class 選擇器

BAD
treecell.indented {…}
GOOD
.treecell-indented {…}
BEST
.hierarchy-deep {…}

4、避免使用多層標籤選擇器。使用 class 選擇器替換,減少css查找

BAD
treeitem[mailfolder="true"] > treerow > treecell {…}
GOOD
.treecell-mailfolder {…}

5、避免使用子選擇器

BAD
treehead treerow treecell {…}
BETTER, BUT STILL BAD 
treehead > treerow > treecell {…}
GOOD
.treecell-header {…}

6、使用繼承

BAD 
#bookmarkMenuItem > .menu-left { list-style-image: url(blah) }
GOOD
#bookmarkMenuItem { list-style-image: url(blah) }

思考

作為一名前端工程師,應該具有「提升 CSS 選擇器性能」的意識,但實際應用中,是否需要完全貫徹這些原則呢?這是一個探索「追求高性能」與「可維護性」兩者平衡的問題。

對於「淘寶」,每個頁面的 DOM 元素超過1000個以上的網站來說,通過限制 CSS 選擇器,改善性能是具有實際意義的。但對於普通網站,我更傾向於保證「語義化」和「可維護性」的前提下,提升 CSS 選擇器性能。

參考
「1」Efficiently Rendering CSS
「2」Writing efficient CSS
「3」Performance Impact of CSS Selectors
「4」CSS Test Creator
「5」高性能CSS
「6」如何撰寫有效率的CSS選擇器