拋開語法,深度剖析CSS前處理器

發表於2016-07-06

背景

現在來說這些,顯得晚了些,大家傾向於去關注最新、最酷的技術,所謂技術的潮流,處理器的概念不算新的,每個前端從業者,應該都使用過或者瞄過兩眼。有些人,試用過之後覺得不錯就一直用並且推廣下去;有些在用了一下之後就放下了;而另外一些,可能看過之後就沒有再用。

不管你是屬於哪一類,其實,我們都應該是保持一種好奇和敬畏之心,去看待它們,畢竟,它們是應需而生,突破思維侷限,打破語言侷限,能夠在一定程度上提高效率,製造便利的好工具。學習之,研究之,是有必要的。

先來看看目前為止CSS的痛點在哪裡?

CSS的出現

1、為結構和表現分離提供了便利。
2、為一個檔案多處引入,改一處多處改,提供了便利。

但是,其最大的缺陷在於,完全靜態,所有程式碼都需要一板一眼的敲上去。比如:

1、巢狀
2、複用程式碼段
3、模組化開發的不靈活
4、不具備邏輯能力和抽象能力

或許,我們可以動用聰明的頭腦去規劃,去設計,來提高程式碼的可維護性、可讀性和可複用性,但它的硬傷是難以突破的。
前處理器的共同點

大家都知道,處理器不止一種,比如sass、less、stylus等,很多人都問過這麼個問題,我要選哪種好呢?它們有什麼不一樣嗎?首先來看共同點:

1、變數

懂得程式設計的人都知道,變數,就是一個容器,你把一個值存進去,然後各處均可通過引用它去訪問那個值。所以,它適合批量定義和修改。

就是這麼簡單,那麼問題來了,什麼時候適合建立一個變數?

  • 該值至少重複出現了兩次;
  • 該值至少可能會被更新一次;
  • 該值所有的表現都與變數有關(非巧合)。

基本上,沒有理由宣告一個永遠不需要更新或者只在單一地方使用的變數。一般來說,變數長這個樣子的

Fl_6X3L-7TyVQgu3YUgMMJAGEXRk

不同的處理器前面的符號不同。

2、巢狀

來看一張大家都熟悉的程式碼段

FrP58y5vTa2w8kSpcgtBBYRop3ec

明顯可以看出,上面這段程式碼,在選擇器部分,行與行之間都有一些重複,這樣對於我們的書寫帶來很多重複。而處理器所提供的巢狀規則,使得我們免於不斷的去寫已經有了的父容器,可以直接在後面書寫子容器的選擇器,比如可以像這樣:

Fm6kS7emuJW3KfECk9my3DtLeHUt

當程式碼量足夠多的時候,這樣就相當提高效率。

3、模組化管理、易維護

當我們在做中、大型專案的時候,往往一個功能或者模組,會在很多頁面都用到,如果我們把它們單獨的放到所有頁面對應的程式碼中,那麼每次修改,我們都要去改所有的程式碼段,這顯然是低效的,如果將它們放到一個大的公共檔案中,有很可能造成冗餘,或許,你會說,可以放在一個單獨的檔案中,然後引入到頁面中,就行了,這樣是可以,但是會多出來一個檔案的請求,如果有多個,那麼從維護性和效能兩方面考慮的話,就得不償失了。所以,我們可以使用處理器中的@import規則,把複用程式碼段提取,然後在需要的頁面引入,這樣,同樣能達到“一處變、處處變”的效果,而且,不會有多餘的請求發出,一個頁面可以只有一個css檔案即可。

比如這樣:

FrXf6DcnC6EgV527J_5HHOht2f5_

我們可以把一個固定的元件,封裝為一個單獨的檔案,然後在需要的檔案裡引入即可,這樣方便維護,也不會在不需要的頁面裡放多餘的程式碼段。

4、Mixins

在css中,有些時候,需要寫一些不情願,但又不得不去寫的複雜程式碼段,最典型的例子就是CSS3的效果,為了相容不同瀏覽器,加一堆瀏覽器字首。如果使用的頻次較高,這仍然會成為不小的成本。雖然我們可以用編輯器的外掛去快速補全,那麼也是需要重複操作的,何不讓我們只寫一次然後都複用之呢?

Mixins給我們提供的就是實現大段樣式的重用,我們可以把需要寫的東西事先封裝起來,然後給其數值部分設定為引數,這樣,我們在需要用到類似東西的時候,就可以直接呼叫已經定義好的mixin,對於尺寸大小的不同,只需要傳入不同引數即可。

拋開語法,深度剖析CSS前處理器

上面列出了一個引數的例子,我們知道,css屬性裡面很多時候不止一個值,這裡也不侷限只是一個屬性,所以多個引數也是允許的,這樣大家可能還會有個困惑,多個引數我是需要嚴格按照順序書寫嗎?其實不必,sass給我們提供了 $name: value 的格式,比如我們可以這樣定義:

FnNEBxMApKEfwVWwycJgJg17cKMW

只要對應的設定value就好。

5、extend擴充套件/繼承

這可以說是處理器的一個亮點,你定義了一個類,後面如果有另一個類需要用到和已經定義了的類同樣的屬性和值,那麼你可以通過@extend來直接引用已經定義的類,來再次使用它定義過的規則。

這樣不會多出來很多重複程式碼段嗎?可能你跟我有過同樣的困惑,其實它生成的,是一個群組選擇器,也就是多個類共用一段css樣式規則,這樣做的好處是,在你想定義有共性又有差異的一組元素時,不需要寫多個類,只需要寫它單獨定義的類即可。

我個人認為,這樣的寫法,如果是在數量不多的時候,跟提取一個基類,再追加擴充套件類的成本是差不多的。並顯示不出優勢在哪裡,只不過是多寫了幾個類在html裡還是css裡,那麼如果是html的重複單元數量很多,差異項又不多的時候,使用這種方法是會更好一些,因為不需要有跟html的每一項進行繫結的基類了。亮點就在於你所定義的這個可以擴充套件的類,是不需要新增到html裡面的,只是在編譯之前的sass層面用來定義了一段公共塊便於引用。比如:

拋開語法,深度剖析CSS前處理器

拋開語法,深度剖析CSS前處理器

此處“.error”這個類,就是sass層面所定義的複用類,不必使用在html中。

那麼問題來了,我們是否有必要去定義一個從來不需要在html中使用的類?可能你覺得它的影響微乎其微,但從sass3.2之後,追求極致的開發者還是給出了對應的特性,那就是“%placeholder”,選擇器佔位符,有了它之後,我們就不需要定義一個沒有實際作用的類了,當然,你可以選擇去定義一個類並使用它,都隨你~

6、運算
涉及到運算的,肯定就是數字相關,加減乘除,那麼能進行怎樣的運算呢?如下:

Fsnmc0rHJZ6-4HhY_r2icJC5OIg9

上圖所示,可以用屬性名直接計算,也可以使用數值,數值當中的單位需要統一,也可以預設,甚至你可以寫一個不存在的單位,編譯的時候都不會報錯,但如果前後不同,就會報錯了。

應用場景:

猛一想,沒有哪裡需要用到,但是,我們仍然是落入了慣性思維的圈子,其實平時的很多計算我們都是心算或者計算器來算,然後寫到樣式裡面了,比如說,當一個容器需要被幾等分的時候,每一項需要多少;當一種佈局需要一邊定寬一邊自適應時,自適應的這邊應該是多寬。等等,這些都是可以使用運算來搞定的,維護起來也更容易些。

其實現在css3已經加入了”calc()”這個具備計算能力的函式,但是,在某些仍不支援它的瀏覽器中,前處理器的計算能夠彌補這一不足。

7、函式

我們知道在很多程式語言裡都有函式的概念,但早期的css是沒有的,那麼前處理器就給我們提供了一些函式,比如:

FpJ5RBtEsxzXrjmAMpOtSb8bSGoy

這裡定義了兩個變數和一個函式,函式有一個引數“$n”,通過編譯,執行了函式之後,有了右邊的css程式碼。
當然,這是個自定義的函式,前處理器有自己的一些內建函式,比如:

顏色函式:

rgb($red,$green,$blue):根據紅、綠、藍三個值建立一個顏色;

rgba($red,$green,$blue,$alpha):根據紅、綠、藍和透明度值建立一個顏色;

red($color):從一個顏色中獲取其中紅色值;

數字函式:

percentage($value):將一個不帶單位的數轉換成百分比值;

round($value):將數值四捨五入,轉換成一個最接近的整數;

ceil($value):將大於自己的小數轉換成下一位整數;

floor($value):將一個數去除他的小數部分;

以上列出的是sass中有的,是不是似曾相識?還有其他非常多的函式,多到什麼程度呢,多到你懷疑它們是用來幹嘛的~有興趣,可以輕戳這裡瞭解http://sass-lang.com/documentation/Sass/Script/Functions.html

是的,即使我鼓勵大家用前處理器,但是不得不說,平時我們的專案中用不到這麼多,所以,大家大概知道就好。

8、條件判斷

提起條件判斷,我們就會想到if…else…那麼,css中有需要使用它的地方?剛開始我也是覺得沒有,因為很多需要判斷的地方,我們都交給js去做了,對吧?所以,還是有用武之地的。比如:

當瀏覽器版本低於IE9的時候,使用哪段樣式;當我們某個元素需要切換狀態的時候,使用哪段樣式。等等。就像下面這樣。

FjqKhB5vIv5A4agsBwUwoszCtGCO

9、迴圈

迴圈的出現意味著存在本不可能出現在Sass中的複雜邏輯。在使用迴圈之前,務必確定這麼做是有道理的,並且確認這麼做可以解決問題。

我們會好奇的是,在哪裡迴圈,用來幹嘛?

說實話,一時能想到的用處真的不多,當我們需要用到它的時候,其實和js裡面的迴圈用途類似,需要處理一組有著相同性質且有規律的東西,比如,一個列表的li,一個表格的tr、td,或者較有規律的雪碧圖,再或者,你在自己能夠駕馭的範圍內自定義的一個屬性或屬性值列表。
比如像這樣:

FnPFbRDCEdIR_ax2zUrYpgFjoRfX

哇偶,酷斃了,是不是有些沒想到還可以這樣?其實從這裡面就可以更明顯的看出css和一些傳統程式語言的差距在哪裡,重複的事情,只做一次。

是什麼讓你還在猶豫?我猜你可能有以下幾處困惑:

1、變數的特殊化

這一點,是我本人,也是同事,都存在過的顧慮——我現在定了一個變數,全站都用,那如果我需要修改其中一處,或者產品經理非要改掉其中一處,怎麼辦?

曾經,我以為這個就是它的一個缺陷,後來才知道是淺嘗輒止就否定它。真實情況是這樣:

FkUvMLS_2RFCY3iJhJwrrfxgQw1Z

從圖中可以看出,sass能夠輕鬆搞定這種特殊化,這就是作用域的問題,全域性變數和區域性變數,和其他程式語言裡的沒什麼不同~就是這麼簡單。

2、偽類、偽元素、容器外部巢狀,這些怎麼辦?

怎麼辦呢?,一個“&”就可以解決啦!下面看是如何實現的。

FjIpQqnRdNOdFUvLmtuUiXZdf46b

這是偽類和偽元素,那麼容器的外部巢狀呢?這個相信你也有想過對麼,當我寫了一大串的巢狀之後,如果需要在外部加父容器進行限制的話,難道還要在外面套一層嗎,顯得太low了吧,其實,你可以這樣:

FgykpdhVjHdqmWLCS0vDXQ_lZX3Q

只要把你想要套在外面的選擇器寫在前面,後面跟“&”就可以搞定了。

3、選擇器層級過多

使用過前處理器的人,應該都有過這方面的擔心,因為這個時候巢狀是很隨意且輕鬆的,寫一個最外面的父選擇器,然後就一層層的往裡套了,就有可能導致,生成的css程式碼出現層級過多的問題。其實就這個問題的話,個人以為,首先要從源頭解決,什麼是源頭,就是你對自己程式碼結構的規劃,html結構的合理精簡,一般情況下,不會出現多於4層的容器巢狀,當然,如果你是從頁面容器到最裡面一層數的話,那超過的可能性就很大了,但覺得沒多少人會這麼去寫吧?所以,按照模組化,元件化的思想去組織程式碼,就不會出現層級過多的問題。

4、生成冗餘程式碼

這一點是我看到sass為我們提供了複用便利之後的直接反應,如果複用起來那麼容易,我們會不會太依賴之,反而生成了很多沒必要的冗餘程式碼段呢?在這一點上,sass的確是沒有提供方案的,從某種程度上來說,也不需要提供吧,這是一個需要我們自己去把控的東西。

mixin,何時使用mixin?

先來一段官方描述:如果你發現自己在不停地重複一段樣式,那就應該把這段樣式構造成優良的混合器,尤其是這段樣式本身就是一個邏輯單元。再為其新增一個展示性的描述,能夠清晰明瞭的看出它是用來幹嘛的。

顯然,你首先會想到的是需要各種瀏覽器廠商字首的CSS3程式碼。一個好的事情是,mixin本身不會被編譯到css程式碼中。
在實踐過程中,我個人並不是完全贊同這種“重複樣式就構造混合器”的做法,我們權當這是一種功能描述,在適合使用的時候使用。實際當中,個人更傾向於構建一個常用程式碼庫,當需要使用的時候,為結構新增可用的類,這樣既方便使用,也不會造成明顯的效能問題,也不會多個地方不斷在重複一個程式碼段。

適合使用mixin的地方在我看來是應該存在變化的,比如,我們不能保證每處的圓角都一樣,每處的陰影都一樣,每處的漸變都一樣,當它們不一樣時,很多時候只是數值改變,而性質未變,這就適合使用帶引數的mixin來搞定了。

extend繼承的工作細節

跟變數和混合器不同,繼承不是僅僅用樣式替換@extend處的程式碼那麼簡單,繼承只會在生成CSS時複製選擇器,而不會複製大段的CSS屬性,其實這一點上面已經說過了,這裡再提及一下。

5、@import效能問題?

如果你有這個疑問,那麼又先入為主了哈。我們可以先來回顧一下CSS的“@import”會帶來什麼問題。

很多關於效能的建議,都說盡量避免使用@import,因為這樣做會導致CSS無法並行載入,使用@import引用的檔案只有在引用它的那個css檔案被下載、解析之後,瀏覽器才會知道還有另外一個css需要下載,這時才去下載。

好的,原因知道了,有問題我們們就有對策是吧,我們在自己定義的所有樣式規則之前引用不就可以了?而且再退一步說,誰告訴你sass編譯完了之後還存在@import這麼個東西的?,它只是在編譯之前方便我們編寫程式碼的一種方法而已,是不是覺得上當了?~所以,完全不用擔心咯。

你可能忽略的一些點:

1、屬性巢狀

你很容易就會知道選擇器之間的巢狀,但是,不僅是選擇器,屬性也是可以巢狀的哦,如下:

Fm1X2anqddySE8gCgqwFxurAay1k

FvD-mOJa7ajVdriBx0QW4uV95-Mj

2、!default、!global

寫css的時候,我們都會面臨著一些維護問題,比如,類名的定義,衝突的避免,sass只是變著法的在寫css,所以,問題是同樣的,特別是在團隊合作的時候。所以當你給定義變數的時候,給變數加上了!default,那麼其他開發者定義了自己要用的變數之後,再引入了你的庫,就不會擔心二者有衝突,比如這樣:

FmRP1OFZFs71QodffnqbvqovK1-X

FgKDE6xLIj8SbunFUk4T1gtU2QRh

哪裡不對勁呢,寫在後面的變數權重反而更低?是的,!default的作用在這裡就是降級讓路,定義一個預設變數值,別的值都可以覆蓋它。

而”!global”的作用恰恰相反,是用來升級覆蓋,回到前面看過的一個例子再來看

FrwbfVjGGt8ieUZJNlk-AEUbPxEu

本來區域性變數只是在區域性起作用,是不會改變全域性的,但是這裡加上了“!global”了之後,.sub-wrap-three的值變成了.sub-wrap-two裡面定義的300px。

3、 巢狀匯入

通過前面的內容,我們已經知道了,sass是可以匯入我們封裝起來的模組樣式的,其實不止如此,還可以在定義的樣式當中進行匯入。比如這樣:

Fgo1NZoNxUrnV8METrJIjz-NsPIv

FuX-5V52yfOK5UNC5AooLpB8oYUw

4、list和map

其實上面那麼多東西,就足以令任何一個頁面仔頭大了,可是sass仍不止這些,還有比較複雜的資料結構,比如:list和map。

list,就相當於js中的陣列。用來定義一組可以訪問和使用的值。比如:

FvJ8v78pR-7Irc7s7O9TDstah3C4

map,是一種對映任何型別的鍵值對。跟什麼差不多呢,你猜對了,json~比如:

Fu1XihhNLPZmfqLyK6hSTafKfuGV

5、警告和錯誤

什麼鬼,還有完沒完?這些又是用來幹嘛的?

@debug; @warn; @error,就是這三位。
@debug,主要是用作除錯 SassScript,這個不是我們關注的重點。
@warn,和我們通常的認知差不多,不會把程式給卡住無法執行,而是能夠預測風險並提醒開發者。
@error,將會中斷編譯器的下一步程式。基本上,它們中斷編譯並像堆疊跟蹤一樣在輸出流中顯示相關資訊,這對除錯很有幫助。

說了這麼多,該如何選擇?

先來看看簡史:

Sass 的第一次提交還要追溯到距今八年之久的 2006 年底。由Ruby語言編寫。

Less 由 Alexis Sellier 於 2009 年建立。使用js編寫。既可以在 客戶端 上執行 (支援IE 6+, Webkit, Firefox),也可以藉助Node.js或者Rhino在服務端執行。

stylus的第一次提交是在2010年11月。來自Node.js社群。

從這個時間線可以看出,sass最早,目前三者都有人使用。其中,sass和less都有官網的中文翻譯。
如下:

sass http://www.sass.hk/ sass設計指南 http://sass-guidelin.es/zh/
less http://less.bootcss.com/
而stylus沒有,只有張鑫旭早在2012年翻譯的中文文件 http://www.zhangxinxu.com/jq/stylus/

他也寫過一篇文章,文中說,他沒有選用sass的原因是ruby http://www.zhangxinxu.com/wordpress/2012/06/stylus-nodejs-expressive-dynamic-robust-css/。
顯然,從實際使用上來說,是不需要用到ruby的,故而,不應成為大家不用它的原因。

所以,其實你選用哪種都可以,就目前來看,sass是最強大,也是被很多人推薦使用的,bootstrap的新版也是拋棄less投奔sass而去,既然差別不大,何不選一個更好用的呢?~

工具

前處理器已經是css的工具了,那麼它還有工具嗎?答案是肯定的,比如,sass的工具compass,less的工具est。這些工具,為預處理提供了很多封裝好的東西可以直接拿來用,可以說,是幫你懶到家了哈。詳情大家自己點選連結看哈,就不多說了。

如果實在不感冒呢?

是的,你可能會覺得才華橫溢的你用css完全能夠勝任現在的工作,那麼為什麼還要去了解前處理器看起來那麼複雜的語法(其實不復雜~),不妨可以使用它的部分特性,比如巢狀、mixin、模組化等等,總之,不需要排斥它,它可以給你帶來一些便利,又何樂而不為呢?~

相關文章