高效的CSS已經不是一個新話題,也不是一個我非得重拾的話題,但是,它卻是自我在SKY工作以後,真正感興趣並始終關注的一個話題。
很多人或者忘記了,或者僅僅是沒有意識到,CSS可以是高效的也可能導致低能。然而,我們可以不考慮當你自認為會的太少而使用了低效的CSS這種情況。
這些規則只真正用在性能要求很高的網站上,這些網站對速度要求很高,任何一個頁面可能含有成百上千個DOM元素。但是實踐出真理,不管你是在打造下一個facebook 還是在開發一個本地的展示網頁,多學點總是好的....
CSS 選擇器
CSS 選擇器對我們大多數人來說並不新鮮,較基礎的選擇器分別是類型(如 div),ID(如#header)和類(如.tweet)。
較不尋常的包括基礎的偽類(如:hover)和更復雜的CSS3以及 '正則'(‘regex’)選擇器,比如:first-childor[class^="grid-"]。
選擇器具有固有效率,引用Steve Souders的話來說,較有效到較不有效的CSS選擇器的順序是這樣的:
ID,如#header
Class, 如.promo
Type, 如div
Adjacent sibling, 如h2 + p
Child, 如li > ul
Descendant,如ul a
Universal,即*
Attribute, 如[type="text"]
Pseudo-classes/-elements, 如a:hover
引用自Steve Souders的Even Faster網站
認識這很重要,雖然一個ID技術上更快而且表現更優,但幾乎都不這樣用。用Steve Souders的CSS測試器,我們可以發現一個 ID 選擇器 和 一個類選擇器 在再渲染速度方面差別很小。
在一台Windows機器上的Firefox6中,我獲得了關於一個簡單的類選擇器的平均再渲染數據。ID選擇器給出了12.5的平均值,所以實際上它要比一個類再渲染得慢一點。
ID與類之間的速度差異幾乎是不相干的。
對一個類型(<a>)的選擇測試,相比一個類或ID給出了慢得多的再渲染。
對一個層次非常多的子孫選擇器的測試給出了 大約440的數值!
通過這我們可以發現IDs/classes與types/descendants的差別非常巨大...它們自身之間的差異很細微。
注意 這些數值在不同的機器和浏覽器變化非常大。我極力建議你自己運行一下。
組合選擇器
你可以單獨使用一種標准選擇器,如#nav,來選擇所有以"nav"為ID的元素,你也可以使用組合選擇器,如#nav a,來選擇任何在ID為’nav’的元素裡面的鏈接元素。
現在,我們從左到右讀這個組合標簽。我們先找到#nav ,然後再找到裡面的元素。但是我們的浏覽器不是這樣解析的,它是從右到左來解析這些組合選擇器的。
當我們看到#nav裡面有個a,而浏覽器看到的卻是有個a在 #nav裡面,這些細微的差異對浏覽器的性能有重大影響,同事學習他們是很有價值的。
如果想知道浏覽器這樣解析的原因,請參考this discussion on Stack Overflow.
對浏覽器來說,從最右邊的元素(它最想渲染的元素)開始,然後回溯到DOM樹比從DOM樹的最高層開始選擇向下尋找,甚至可能達不到最右邊的選擇器(關鍵的選擇器)要高效。
這對CSS選擇器的性能有重大影響....
關鍵選擇器
這裡討論的關鍵選擇器, 是處在復雜選擇器最右端的選擇器,也是浏覽器最先解析的選擇器。
讓我們回到討論開始的地方,哪種選擇器最高效?哪種選擇器作為關鍵選擇器會影響選擇器的性能;當我們書寫CSS代碼的時候,正是這個關鍵選擇器影響選擇器的效率。
一個關鍵選擇器是這樣的:
#content.intro{}
天生高效選擇器如類型選擇器是不是就會有更高的性能?浏覽器會尋找.Intro的所有實例(數量不會很多),然後向上查找DOM樹,以確定該關鍵選擇器是否在以“content’”為ID的元素裡面。
然後,以下的選擇器性能就不怎麼好了:
#content*{}
這個選擇器做的工作是這樣子的,它先查找每個頁面(是每個單個的頁面),然後去看看它們是否有一個 #content 的父元素。這是一個非常不高效的選擇器,因為它的關鍵選擇器執行開銷太大了。
運用這些知識我們就能在分類和選擇元素時做更好的選擇。
假設我們有一個非常龐大的頁面,非常的大並且是一個還有一個龐大的網站。在這個也面上,有上百甚至上千哥<a>標簽。社交媒體的鏈接只占很少的一部分,並且包含在ID為#social的<ul>裡面。假設這些鏈接是,Twitter, facebook, Dribble 和 Google+。我們在這個頁面上有四個社交媒體鏈接,還有上百個其他的鏈接。
以下這個選擇器代價很高而且性能不好:
#social a{}
浏覽器將訪問掃描頁面上的上千個鏈接,然後才選擇上#social節點下的四個鏈接。我們的關鍵選擇器匹配了大量的我們並不感興趣的標簽。
為了消除這個問題,我們可以為每一個包含在社交媒體<ul>塊下面的<a>標簽指定更明確和顯式的選擇器.social-link。但是這跟我們所知道的恰恰相反;我們知道了,當我們使用精簡的標簽的時候,盡量不要使用多余的類。
這就是為什麼我覺得性能是如此的有意思;在網頁標准最佳實踐和速度之間,需要一個平衡。
我們通常會這麼寫:
<ul id="social">
<li><a href="#" class="twitter">Twitter</a></li>
<li><a href="#" class="facebook">Facebook</a></li>
<li><a href="#" class="dribble">Dribbble</a></li>
<li><a href="#" class="gplus">Google+</a></li>
</ul>
CSS:
#social a{}
現在我們改為:
<ul id="social">
<li><a href="#" class="social-link twitter">Twitter</a></li>
<li><a href="#" class="social-link facebook">Facebook</a></li>
<li><a href="#" class="social-link dribble">Dribbble</a></li>
<li><a href="#" class="social-link gplus">Google+</a></li>
</ul>
對應的CSS:
#social .social-link{}
這是一個全新的關鍵選擇器,匹配更少的元素,意味著浏覽器可以更快地找到它們並給它們賦予樣式。
同時,實際上,我們可以進一步精簡選擇器,使用.social-link{},而不需要過渡的約束它;閱讀下一章節來了解詳情。
所以,概括的說,你的關鍵選擇器是其中一個決定浏覽器的工作量,所以你需要好好的留意它。
過度修飾的選擇器
現在我們已經了解了關鍵選擇器是什麼,它是後續工作的基礎,我們現在來看看如何進一步優化它。使用更明確的關鍵選擇器就是,你需要避免過度使用修飾選擇器。以下是一個過度修飾的選擇器:
1
Html body .wrapper #content a{}這個選擇太啰唆,起碼有三個選擇器是完全沒必要的。精簡樣式應該為:
#content a{}
那又如何?
這意味著浏覽器需要掃描所有的標簽,檢查它裡面是否包含一個ID叫做“content”的標簽,如此在Html標簽中不停查找。這樣會導致浏覽器做了多余的檢查工作,並且是完全沒必要的工作。知道了這點後,我們可以看看下面更接近實際的例子:
#nav li a{}
精簡為:
#nav a{}
我們知道如果標簽a在li標簽裡面,li標簽又在#nav裡面,那麼我們可以從選擇器中去掉li。然後,因為nav擁有一個ID,這個ID是唯一的,所以它賦予的元素是完全不相干的,我們可以去掉ul。
過度修飾的選擇器會導致浏覽器需要做更多的工作;從選擇器中去除不必要的部分,讓你的選擇器更簡潔,性能更高吧。
所有這些都是必要的嗎?
簡短的答案是:可能不是。
長一點的答案是:它依賴於你正在創建的網站。如果你在做你的下一個作品,那麼超越CSS選擇器性能去追尋干淨的代碼吧,因為你其實並不想關注它。
如果你在創建下一版的Amazon, 而頁面速度的微秒變化確實會有不同,那麼可能需要,但即使那樣也可能不。
浏覽器只會在CSS解析速度方面越來越好,甚至移動浏覽器也一樣。你很可能從來也不會注意到浏覽器中的很慢的CSS選擇器,但是...
但是
它仍然正在發生著,不管浏覽器有多麼快,它們仍然必須做所有我們談論到的工作。即使你不需要甚至不想實施其中任何一條,它當然仍是值得知道的東西。記得選擇器可能會代價很高,還有你應該在可能的地方避免使用代價更大的選擇器。那意味著,如果你發現自己寫了一些東西諸如:
div:nth-of-type(3) ul:last-child li:nth-of-type(odd) *{ font-weight:bold }
那麼可能你做得不對。現在我自己在高效選擇器的世界也是一個新手,因此如果我遺漏了什麼,或者你有什麼需要添加的,請在評論裡發表它!
更多關於高效的CSS選擇器
我怎麼推薦Steve Souders的網站與書也不過分。對你需要的延伸閱讀來說,那足夠了。這伙計知道的很多!本文鏈接http://www.cxybl.com/html/wyzz/CSS/20130915/40122.Html